Package cdp provides type-safe bindings for the Chrome DevTools Protocol (CDP), written in the Go programming language.

cdp

Build Status Coverage Status Go Report Card GoDoc

Package cdp provides type-safe bindings for the Chrome DevTools Protocol (CDP), written in the Go programming language. The bindings are generated (by cdpgen) from the latest tip-of-tree (tot) protocol definitions and are mainly intended for use with Google Chrome or Chromium, however, they can be used with any debug target (Node.js, Edge DevTools Protocol, Safari, etc.) that implement the protocol.

This package can be used for any kind of browser automation, scripting or debugging via the Chrome DevTools Protocol.

A big motivation for cdp is to expose the full functionality of the Chrome DevTools Protocol and provide it in a discoverable and self-documenting manner.

Providing high-level browser automation is a non-goal for this project. That being said, cdp hopes to improve the ergonomics of working with the protocol by providing primitives better suited for Go and automating repetitive tasks.

Features

  • Discoverable API for the Chrome DevTools Protocol (GoDoc, autocomplete friendly)
  • Contexts as a first-class citizen (for timeouts and cancellation)
  • Simple and synchronous event handling (no callbacks)
  • Concurrently safe
  • No silent or hidden errors
  • Do what the user expects
  • Match CDP types to Go types wherever possible
  • Separation of concerns (avoid mixing CDP and RPC)

Installation

$ go get -u github.com/mafredri/cdp

Documentation

See API documentation for package, API descriptions and examples. Examples can also be found in this repository, see the simple, advanced, logging and incognito examples.

Usage

The main packages are cdp and rpcc, the former provides the CDP bindings and the latter handles the RPC communication with the debugging target.

To connect to a debug target, a WebSocket debugger URL is needed. For example, if Chrome is running with --remote-debugging-port=9222 the debugger URL can be found at localhost:9222/json. The devtool package can also be used to query the DevTools JSON API (see example below).

Here is an example of using cdp:

package main

import (
	"bufio"
	"context"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"time"

	"github.com/mafredri/cdp"
	"github.com/mafredri/cdp/devtool"
	"github.com/mafredri/cdp/protocol/dom"
	"github.com/mafredri/cdp/protocol/page"
	"github.com/mafredri/cdp/rpcc"
)

func main() {
	err := run(5 * time.Second)
	if err != nil {
		log.Fatal(err)
	}
}

func run(timeout time.Duration) error {
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	// Use the DevTools HTTP/JSON API to manage targets (e.g. pages, webworkers).
	devt := devtool.New("http://127.0.0.1:9222")
	pt, err := devt.Get(ctx, devtool.Page)
	if err != nil {
		pt, err = devt.Create(ctx)
		if err != nil {
			return err
		}
	}

	// Initiate a new RPC connection to the Chrome DevTools Protocol target.
	conn, err := rpcc.DialContext(ctx, pt.WebSocketDebuggerURL)
	if err != nil {
		return err
	}
	defer conn.Close() // Leaving connections open will leak memory.

	c := cdp.NewClient(conn)

	// Open a DOMContentEventFired client to buffer this event.
	domContent, err := c.Page.DOMContentEventFired(ctx)
	if err != nil {
		return err
	}
	defer domContent.Close()

	// Enable events on the Page domain, it's often preferrable to create
	// event clients before enabling events so that we don't miss any.
	if err = c.Page.Enable(ctx); err != nil {
		return err
	}

	// Create the Navigate arguments with the optional Referrer field set.
	navArgs := page.NewNavigateArgs("https://www.google.com").
		SetReferrer("https://duckduckgo.com")
	nav, err := c.Page.Navigate(ctx, navArgs)
	if err != nil {
		return err
	}

	// Wait until we have a DOMContentEventFired event.
	if _, err = domContent.Recv(); err != nil {
		return err
	}

	fmt.Printf("Page loaded with frame ID: %s\n", nav.FrameID)

	// Fetch the document root node. We can pass nil here
	// since this method only takes optional arguments.
	doc, err := c.DOM.GetDocument(ctx, nil)
	if err != nil {
		return err
	}

	// Get the outer HTML for the page.
	result, err := c.DOM.GetOuterHTML(ctx, &dom.GetOuterHTMLArgs{
		NodeID: &doc.Root.NodeID,
	})
	if err != nil {
		return err
	}

	fmt.Printf("HTML: %s\n", result.OuterHTML)

	// Capture a screenshot of the current page.
	screenshotName := "screenshot.jpg"
	screenshotArgs := page.NewCaptureScreenshotArgs().
		SetFormat("jpeg").
		SetQuality(80)
	screenshot, err := c.Page.CaptureScreenshot(ctx, screenshotArgs)
	if err != nil {
		return err
	}
	if err = ioutil.WriteFile(screenshotName, screenshot.Data, 0644); err != nil {
		return err
	}

	fmt.Printf("Saved screenshot: %s\n", screenshotName)

	pdfName := "page.pdf"
	f, err := os.Create(pdfName)
	if err != nil {
		return err
	}

	pdfArgs := page.NewPrintToPDFArgs().
		SetTransferMode("ReturnAsStream") // Request stream.
	pdfData, err := c.Page.PrintToPDF(ctx, pdfArgs)
	if err != nil {
		return err
	}

	sr := c.NewIOStreamReader(ctx, *pdfData.Stream)
	r := bufio.NewReader(sr)

	// Write to file in ~r.Size() chunks.
	_, err = r.WriteTo(f)
	if err != nil {
		return err
	}

	err = f.Close()
	if err != nil {
		return err
	}

	fmt.Printf("Saved PDF: %s\n", pdfName)

	return nil
}

For more information, consult the documentation.

Acknowledgements

The Go implementation of gRPC (grpc-go) has been a source of inspiration for some of the design decisions made in the cdp and rpcc packages. Some ideas have also been borrowed from the net/rpc package from the standard library.

Resources

Owner
Mathias Fredriksson
Mathias Fredriksson
Comments
  • Geolocation

    Geolocation

    Do you have any advice how to do geolocation on headless chrome?

    On regular chrome I can run this in the console: navigator.geolocation.getCurrentPosition(function (position) { console.log(position); });

    And it will pop up a dialog asking for permission, then print the coordinates in the console.

    On headless chrome I don't get any indication of a dialog box opening and I never receive a response.

    I tried adding an emulated position thinking that maybe by default headless chrome doesn't have a position:

            err = c.Emulation.SetGeolocationOverride(ctx, emulation.NewSetGeolocationOverrideArgs().SetLatitude(11.0).SetLongitude(12.0).SetAccuracy(13.0))
            if err != nil {
                    log.Fatal(err)
            }
    

    And I don't receive any errors, however again when I try to query the geolocation I don't get any indication of a dialog box, and there is never a response to my query.

  • Getting network requests or current page's URL

    Getting network requests or current page's URL

    I'm trying to use cdp to monitor network requests to follow a chain of redirects. Ideally I would manage to get all the visited URLs, but in the worst case I need the very last opened URL. I understand the best way to do this with Chrome Devtools protocol is to monitor for Page.frameNavigated events and extract the frame URL. Quickly searching through the code it seems this event is not supported? What would be the best way to implement this using cdp? Thanks for your work!

  • Support target session connections

    Support target session connections

    This PR improves the use case of communicating with targets via the Target domain (SendMessageToTarget / ReceivedMessageFromTarget).

    ~~This still has a few TODOs and is not production ready (e.g. calling (*Client).Close() multiple times will panic), but it should be ready for some testing.~~

    I would love some feedback. I was working on a bigger improvement to Target management but decided to spin off the session part into it's own package.

    Limitations:

    • Commands issued over the emulated rpcc.Conn will always wait for confirmation from the original websocket that the command was delivered
      • E.g. running go c.Page.Enable(nil); go c.Network.Enable(nil) will work, but only one Target.SendMessageToTarget will be active at a time, and waiting for the response
      • This can be improved once rpcc supports async invocations (ref #18)

    Example usage:

    // Initialize a new session client.
    c := cdp.NewClient(conn) // cdp.Client with websocket connection.
    
    sc, err := session.NewClient(c)
    if err != nil {
    	// Handle error.
    }
    defer sc.Close() // Cleanup.
    
    // Establish a new session connection to targetID.
    pageConn, err := sc.Dial(context.TODO(), targetID)
    if err != nil {
    	// Handle error.
    }
    defer pageConn.Close()
    
    // Use the session connection.
    pageClient := cdp.NewClient(pageConn)
    err = pageClient.Page.Enable(context.TODO())
    // ...
    
  • DevTools.Create() doesn't work with headless chromium

    DevTools.Create() doesn't work with headless chromium

    Hi,

    I'm trying to build a small tool that takes screenshots of a number of URLs. For that I'd like to use a headless chromium running on a server (without a display). I'd also like to do that with ~10 URLs in parallel, so I'd like to create 10 tabs in the browser and use them from different goroutines in parallel.

    With a GUI chromium that works great by creating a new "Page" for every goroutine:

    page, err := devt.Create(ctx)
    

    However on a headless chromium that fails:

    panic: target CreateURL: Could not create new page
    

    Turns out that the code just requests /json/new, which does not work on headless chromium. The way to go is to use createTarget() to create a new browsing environment, reference: https://groups.google.com/a/chromium.org/forum/#!topic/headless-dev/HLeIEn5V0DA

    Can I do that already with cdp? I'm also willing to help implement this, if you guide me through the code base a bit :)

  • Print to PDF: issues with header and footer templates

    Print to PDF: issues with header and footer templates

    Hello @mafredri ,

    Thank you for the library, great job!

    I'm currently trying to add a header and a footer when printing a page to PDF, but I've encountered some weird issues.

    print, err := c.Page.PrintToPDF(
    	html.Context,
    	page.NewPrintToPDFArgs().
    		SetLandscape(false).
    		SetPrintBackground(true).
    		SetMarginTop(0).
    		SetMarginBottom(0).
    		SetMarginLeft(0).
    		SetMarginRight(0).
    		SetDisplayHeaderFooter(true).
    		SetHeaderTemplate(headerString).
    		SetFooterTemplate(footerString).
    		SetPaperWidth(8.27).
    		SetPaperHeight(11.7),
    	)
    

    Issues

    1. When both header and footer templates are set, none of them appear in the resulting PDF
    2. When only the header template is set, it is displayed at the bottom (like a... footer?) using what looks like a default template (date + title)
    3. When only the footer template is set, it is displayed at the top (like a... header) using again what looks like a default template (URL + current page / total pages)

    Additional information

    The page is actually a local file, nothing special about it. The margins are specified in CSS:

    @page { 
        margin: 25mm 25mm; 
    }
    

    Header template:

    <div style="background-color: green;">
     <span class="pageNumber"> of <span class="totalPages"></span>
    </div>
    

    Footer template:

    <div style="background-color: red;">
     <span class='pageNumber"> of <span class="totalPages"></span>
    </div>
    

    System information

    Golang version: 1.11.2 Library version: 0.20.0 Chrome version: 70.0.3538.110

  • Interceptionid is  repeat

    Interceptionid is repeat

    HI ,sir ,this is my scrapy html url ,the interception is repeated ,please see this result will influences my use result ,thanks @mafredri @rtomayko @fd0 :

    2017/12/07 13:46:10 http://m.elongstatic.com/promotions/wireless/uploadImages/images149370993339413.jpg
    2017/12/07 13:46:10 http://www.elongstatic.com/common/js/noexpire/s_code.js?20171129181150
    2017/12/07 13:46:10 {"interceptionId":"id-26","request":{"url":"http://www.elongstatic.com/common/js/noexpire/s_code.js?20171129181150","method":"GET","headers":{"User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3264.0 Safari/537.36","X-DevTools-Emulate-Network-Conditions-Client-Id":"(DE0A908A06D7D765113ED0BF265D0147), (DE0A908A06D7D765113ED0BF265D0147)","Accept":"*/*"},"initialPriority":"High","referrerPolicy":"no-referrer-when-downgrade"},"frameId":"(DE0A908A06D7D765113ED0BF265D0147)","resourceType":"Script","isNavigationRequest":false,"redirectUrl":"http://m.elongstatic.com/wwwelongstatic/common/js/expire/s_code.js?20171129181150"}
    2017/12/07 13:46:10 {"interceptionId":"id-87","request":{"url":"http://ihotel.elong.com/HotDataWindow_Region.html?callback=jQuery1111024597974242530696_1512625570090\u0026_=1512625570096","method":"GET","headers":{"User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3264.0 Safari/537.36","X-DevTools-Emulate-Network-Conditions-Client-Id":"(DE0A908A06D7D765113ED0BF265D0147), (DE0A908A06D7D765113ED0BF265D0147)","Accept":"*/*"},"initialPriority":"Low","referrerPolicy":"no-referrer-when-downgrade"},"frameId":"(DE0A908A06D7D765113ED0BF265D0147)","resourceType":"Script","isNavigationRequest":false}
    2017/12/07 13:46:10 http://ihotel.elong.com/HotDataWindow_Region.html?callback=jQuery1111024597974242530696_1512625570090&_=1512625570096
    2017/12/07 13:46:10 http://www.elongstatic.com/common/js/noexpire/s_code.js?20171129181150
    2017/12/07 13:46:10 {"interceptionId":"id-26","request":{"url":"http://www.elongstatic.com/common/js/noexpire/s_code.js?20171129181150","method":"GET","headers":{"User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3264.0 Safari/537.36","X-DevTools-Emulate-Network-Conditions-Client-Id":"(DE0A908A06D7D765113ED0BF265D0147), (DE0A908A06D7D765113ED0BF265D0147)","Accept":"*/*"},"initialPriority":"High","referrerPolicy":"no-referrer-when-downgrade"},"frameId":"(DE0A908A06D7D765113ED0BF265D0147)","resourceType":"Script","isNavigationRequest":false,"redirectUrl":"http://m.elongstatic.com/wwwelongstatic/common/js/expire/s_code.js?20171129181150"}
    2017/12/07 13:46:10 {"interceptionId":"id-26","request":{"url":"http://www.elongstatic.com/common/js/noexpire/s_code.js?20171129181150","method":"GET","headers":{"User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3264.0 Safari/537.36","X-DevTools-Emulate-Network-Conditions-Client-Id":"(DE0A908A06D7D765113ED0BF265D0147), (DE0A908A06D7D765113ED0BF265D0147)","Accept":"*/*"},"initialPriority":"High","referrerPolicy":"no-referrer-when-downgrade"},"frameId":"(DE0A908A06D7D765113ED0BF265D0147)","resourceType":"Script","isNavigationRequest":false,"redirectUrl":"http://m.elongstatic.com/wwwelongstatic/common/js/expire/s_code.js?20171129181150"}
    2017/12/07 13:46:10 http://www.elongstatic.com/common/js/noexpire/s_code.js?20171129181150
    2017/12/07 13:46:10 {"interceptionId":"id-26","request":{"url":"http://www.elongstatic.com/common/js/noexpire/s_code.js?20171129181150","method":"GET","headers":{"User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3264.0 Safari/537.36","X-DevTools-Emulate-Network-Conditions-Client-Id":"(DE0A908A06D7D765113ED0BF265D0147), (DE0A908A06D7D765113ED0BF265D0147)","Accept":"*/*"},"initialPriority":"High","referrerPolicy":"no-referrer-when-downgrade"},"frameId":"(DE0A908A06D7D765113ED0BF265D0147)","resourceType":"Script","isNavigationRequest":false,"redirectUrl":"http://m.elongstatic.com/wwwelongstatic/common/js/expire/s_code.js?20171129181150"}
    2017/12/07 13:46:10 http://www.elongstatic.com/common/js/noexpire/s_code.js?20171129181150
    2017/12/07 13:46:10 {"interceptionId":"id-26","request":{"url":"http://www.elongstatic.com/common/js/noexpire/s_code.js?20171129181150","method":"GET","headers":{"User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3264.0 Safari/537.36","X-DevTools-Emulate-Network-Conditions-Client-Id":"(DE0A908A06D7D765113ED0BF265D0147), (DE0A908A06D7D765113ED0BF265D0147)","Accept":"*/*"},"initialPriority":"High","referrerPolicy":"no-referrer-when-downgrade"},"frameId":"(DE0A908A06D7D765113ED0BF265D0147)","resourceType":"Script","isNavigationRequest":false,"redirectUrl":"http://m.elongstatic.com/wwwelongstatic/common/js/expire/s_code.js?20171129181150"}
    2017/12/07 13:46:10 http://www.elongstatic.com/common/js/noexpire/s_code.js?20171129181150
    2017/12/07 13:46:10 {"interceptionId":"id-26","request":{"url":"http://www.elongstatic.com/common/js/noexpire/s_code.js?20171129181150","method":"GET","headers":{"User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/64.0.3264.0 Safari/537.36","X-DevTools-Emulate-Network-Conditions-Client-Id":"(DE0A908A06D7D765113ED0BF265D0147), (DE0A908A06D7D765113ED0BF265D0147)","Accept":"*/*"},"initialPriority":"High","referrerPolicy":"no-referrer-when-downgrade"},"frameId":"(DE0A908A06D7D765113ED0BF265D0147)","resourceType":"Script","isNavigationRequest":false,"redirectUrl":"http://m.elongstatic.com/wwwelongstatic/common/js/expire/s_code.js?20171129181150"}
    2017/12/07 13:46:10 http://www.elongstatic.com/common/js/noexpire/s_code.js?20171129181150
    
  • Deadlock with cdp.Sync and incoming notification

    Deadlock with cdp.Sync and incoming notification

    We are seeing what appears to be a deadlock between a cdp.Sync call and an incoming RPC notification. The notification handling goroutine holds Conn.streamMu while trying to acquire syncMessageStore.mu and cdp.Sync does the opposite.

    RPC Notification

    rpcc.(*Conn).notify - holds conn.streamMu rpcc.(*streamClients).write - holds streamClients.mu rpcc.(*syncMessageStore).write - blocking on syncMessageStore.mu

    cdp.Sync

    rpcc.Sync - holds streamClient.mu for each stream rpcc.(*syncMessageStore).subscribe - holds syncMessageStore.mu rpcc.(*Conn).listen - blocking on conn.streamMu

    Backtraces

    goroutine 33 [semacquire, 917 minutes]:
    sync.runtime_SemacquireMutex(0xc0007a6c84, 0x40e100)
    	GOROOT/src/runtime/sema.go:71 +0x3d
    sync.(*Mutex).Lock(0xc0007a6c80)
    	GOROOT/src/sync/mutex.go:134 +0xff
    github.com/mafredri/cdp/rpcc.(*syncMessageStore).write(0xc0007a6c80, 0xc000796420, 0x19, 0xc0048cf080, 0x287, 0x2c0, 0x0)
    	vendor/github.com/mafredri/cdp/rpcc/stream_sync.go:75 +0x81
    github.com/mafredri/cdp/rpcc.(*streamClients).write(0xc0006bc500, 0xc000796420, 0x19, 0xc0048cf080, 0x287, 0x2c0)
    	vendor/github.com/mafredri/cdp/rpcc/stream.go:341 +0x115
    github.com/mafredri/cdp/rpcc.(*Conn).notify(0xc000458000, 0xc000796420, 0x19, 0xc0048cf080, 0x287, 0x2c0)
    	vendor/github.com/mafredri/cdp/rpcc/conn.go:427 +0xd2
    github.com/mafredri/cdp/rpcc.(*Conn).recv(0xc000458000, 0xc0001305f0, 0xc0001305e0)
    	vendor/github.com/mafredri/cdp/rpcc/conn.go:329 +0x12d
    created by github.com/mafredri/cdp/rpcc.DialContext
    	vendor/github.com/mafredri/cdp/rpcc/conn.go:182 +0x334
    
    goroutine 485461 [semacquire, 917 minutes]:
    sync.runtime_SemacquireMutex(0xc00045811c, 0x0)
    	GOROOT/src/runtime/sema.go:71 +0x3d
    sync.(*Mutex).Lock(0xc000458118)
    	GOROOT/src/sync/mutex.go:134 +0xff
    github.com/mafredri/cdp/rpcc.(*Conn).listen(0xc000458000, 0xf77ba1, 0x1e, 0x1114580, 0xc0007a6c80, 0x0, 0x0, 0x0)
    	vendor/github.com/mafredri/cdp/rpcc/conn.go:436 +0x54
    github.com/mafredri/cdp/rpcc.(*syncMessageStore).subscribe(0xc0007a6c80, 0xf77ba1, 0x1e, 0x1114560, 0xc0007f5f80, 0xc000458000, 0x0, 0x0, 0x0)
    	vendor/github.com/mafredri/cdp/rpcc/stream_sync.go:34 +0x1f7
    github.com/mafredri/cdp/rpcc.Sync(0xc018af1200, 0x4, 0x4, 0x0, 0x0)
    	vendor/github.com/mafredri/cdp/rpcc/stream_sync.go:160 +0x25a
    github.com/mafredri/cdp.Sync(0xc000539488, 0x4, 0x4, 0x7fa37a62f2c8, 0xc002dc07a0)
    	vendor/github.com/mafredri/cdp/sync.go:42 +0x146
    
  • websocket: bad handshake

    websocket: bad handshake

    Hi

    I have a containerized version of the golang service using this library to communicate with chrome headless running on a separate container. Both these containers are deployed as services in kuberentes. On a local kubernetes environment running in minikube the golang service is failing to establish the connection with the chrome service. More specifically the devtool.Create call fails with 'websocket: bad handshake' error.

    devt := devtool.New(chromeService) pt, err := devt.Create(ctx)

    The curious case is if i run this golang service on my host system (outside of kubernetes) it is able to connect and operate succefully with the chrome-headless running on minikube.

    Only when it is running inside minikube and trying to communicate with chrome then it fails.

    Kindly advise

  • Dispatch an event using NodeID/BackendID only

    Dispatch an event using NodeID/BackendID only

    Hey,

    is there a way to dispatch click (actually, any event) using only NodeID/BackendID? I know that it's possible using JS eval, but in this case I have to use CSS selectors. But for example, If I have an Node, is there a way to dispatch an event against it? There are mouse events, which theoretically can trigger click event, but I need to get an element location which is challenge too.

  • Strange issue with screenshotting dynamic websites

    Strange issue with screenshotting dynamic websites

    Using this code I can actually screenshot properly most of web pages https://gist.github.com/s3rj1k/0fc459f9c6aa7db42283b805c509e007

    but it fails with pikabu.ru, output is somehow stacked

    proper screenshot can be seen in https://dehtml.com/https://pikabu.ru using phantomjs

    any ideas on how to fix this?

  • Creating & activating a new target does not actually switch targets

    Creating & activating a new target does not actually switch targets

    Hello,

    In attempting to create new targets in the browser (i.e. multiple tabs), I've run into what I believe is a bug, in that when I create a new target and activate it using Target.ActivateTarget, the debugger is still attached to the old target. Strangely, calling Target.CloseTarget will close the newly-created target, but I still cannot seem to attach to it no matter what I do. I've attempted to close all targets when initializing the cdp.Client, but this returns an error and causes any subsequent target creation to fail with an error like rpcc: the connection is closing.

    Here's a gist exhibiting this behaviour (relies on Chrome listening on localhost:9222).

  • Default example not working    dial tcp [::1]:9222: connectex

    Default example not working dial tcp [::1]:9222: connectex

    Hello, i looked on google briefly and i cannot get th sublime_text_9aPOiOtrY5 Do i have to setup any additional stuff to get the devtool working ? here is my console error C:\Users\xpyth\Desktop\dcpTest>go run main.go

    2022/04/25 02:30:11 Get "http://localhost:9222/json/list": dial tcp [::1]:9222: connectex: No connection could be established because the target computer expressly refused it. exit status 1

    I also tried to disable my firewall in case it could cause issues, but did not worked too. Can anyone tell me what i did wrong please ? Thanks in advance !

  • Connection for every thread

    Connection for every thread

    Hi, apologize if its in the docs. I would like to create a link preview rpc API so that client can send a URL and get the title, desc, etc. Should I call devtool.New inside the RPC handler or only once when the service is initialized? How many connections to the devtool can be managed at a time ? I'm planning to use the docker as described here: https://stackoverflow.com/questions/59110235/how-to-run-chromedp-in-docker I am confused if 10k users call my RPC, that means 10k connections to the devtool and how is it going to be handled by the chromedriver thank you

    	ctx2, cancel := context.WithTimeout(context.Background(), 100*time.Second)
    	defer cancel()
    
    	// Use the DevTools HTTP/JSON API to manage targets (e.g. pages, webworkers).
    	devt := devtool.New("http://127.0.0.1:9222")
    	pt, err := devt.Get(ctx2, devtool.Page)
    	if err != nil {
    		pt, err = devt.Create(ctx2)
    		if err != nil {
    			return nil, err
    		}
    	}
    
    	// Initiate a new RPC connection to the Chrome DevTools Protocol target.
    	conn, err := rpcc.DialContext(ctx2, pt.WebSocketDebuggerURL)
    	if err != nil {
    		return nil, err
    	}
    	defer conn.Close() // Leaving connections open will leak memory.
    
    	c := cdp.NewClient(conn)
    
  • rpcc.Conn.Close returns an error if the target is already closed

    rpcc.Conn.Close returns an error if the target is already closed

    rpcc.Conn.Close returns an error if the target is already closed, e.g. by invoking cdp.Client.Target.CloseTarget. This is a bit inconvenient because we have to ignore errors from rpcc.Conn.Close when we close the target in advance, which means that we might miss other interesting errors.

  • Sketch for implementing sessions with flattened protocol.

    Sketch for implementing sessions with flattened protocol.

    Hi again! I made a rough sketch for the flattened protocol. It is really quite rough since I'm a novice with Go, and there are certainly some loose ends, especially around closing connections. I tried my best at keeping the integrity / design of your work intact. And upfront: thank you, I feel I learned quite a bit about how a well designed Go library looks!

    The gist is, I got the unittest for the sessions in sessions_test.go to work with the flattened protocol, so this is why I figured I should post what I have thus far. If the approach in this PR is viable, I'm happy to work on fixing it / finishing it, or if you'd like to take this over that's fine too, or if some other way of getting flat sessions support makes more sense, please bring it on. Thank you. :-)

    Approach taken here:

    • Trying to keep this compatible with existing code, so that upgrading to this version will "just work", but not switch to flattened protocol. To switch, minor tweaks in the client code are required (see session_test.go).
    • The non-flattened sessions create an rpcc.Conn instance which reads/writes to an underlying instance by sending Target.sendMessageToTarget etc. This is still the case; but to support non-flattened sessions this PR adds rpcc.SessionConn, which is much lighter weight - it carries the SessionID and can be closed, but the traffic is sent via it's parent connection, an instance of rpcc.Conn, which keeps track of its rpcc.SessionConn instances.
    • Various methods and structs now have a SessionID field / sessionID argument.
    • A few public method names are new, when I felt that it's better to keep the old one as is for compatibility. E.g., rpcc.Invoke (made rpcc.InvokeRPC with extra param), session.Manager.Dial (made session.Manager.Attach), and in the generated code there's cdp.Client.NewSession to get a client that talks via a specific session.
  • FR: Improvements to session manager

    FR: Improvements to session manager

    I am trying to use the session manager in a context where I want to attach to all existing targets, and then attach to and get messages about detaching from all targets as they are created/destroyed. I have something mostly working, but it is more complex and fragile than I would prefer:

    1. Support enabling https://chromedevtools.github.io/devtools-protocol/tot/Target#method-setAutoAttach. Since there is no way to get rpcc.Conn or cdp.Client for an already attached session, it is difficult to interact with an auto attached session.

    2. Provide access to the current sessionID for an rpcc.Conn or cdp.Client. targetId is deprecated in https://chromedevtools.github.io/devtools-protocol/tot/Target#event-detachedFromTarget, but there is currently no ready way to identify the sessionId for an rpcc.Conn or cdp.Client (it currently can be done by creating a map of sessionId to targetId when attachedToTarget events are fired, except in for the initial client).

  • Protocol version

    Protocol version

    Hi!

    I wonder what's the current version of the CDP protocol the library is using now? Since the protocol is still in flux, I need the number to properly stick to a particular version of Chrome.

A faster, simpler way to drive browsers supporting the Chrome DevTools Protocol.

About chromedp Package chromedp is a faster, simpler way to drive browsers supporting the Chrome DevTools Protocol in Go without external dependencies

Dec 28, 2022
A Devtools driver for web automation and scraping

Overview Documentation | API reference Rod is a high-level driver directly based on DevTools Protocol. It's designed for web automation and scraping.

Dec 30, 2022
A tool for generating self-contained, type-safe test doubles in go

counterfeiter When writing unit-tests for an object, it is often useful to have fake implementations of the object's collaborators. In go, such fake i

Jan 5, 2023
Completely type-safe compile-time mock generator for Go

Mockc Mockc is a completely type-safe compile-time mock generator for Go. You can use it just by writing the mock generators with mockc.Implement() or

Aug 25, 2022
Type-safe assertion helpers for Go

attest attest is a small package of type-safe assertion helpers. Under the hood, it uses cmp for equality testing and diffing. You may enjoy attest if

Aug 30, 2022
Prototype pollution scanner using headless chrome
Prototype pollution scanner using headless chrome

plution Prototype pollution scanner using headless chrome What this is Plution is a convenient way to scan at scale for pages that are vulnerable to c

Jan 1, 2023
Generate PlantUML diagrams from Chrome or Firefox network inspections

hoofli Generate PlantUML diagrams from Chrome or Firefox network inspections This tool reads browser HAR files stored on your local disk and transform

Nov 15, 2022
A Comprehensive Coverage Testing System for The Go Programming Language
A Comprehensive Coverage Testing System for The Go Programming Language

goc 中文页 | goc is a comprehensive coverage testing system for The Go Programming Language, especially for some complex scenarios, like system testing c

Jan 8, 2023
Hamcrest matchers for the Go programming language

Note: This has not been maintained and/or updated since 2011. Perhaps consider corbym/gocrest, instead. Introduction Hamcrest is a fluent framework fo

Sep 27, 2022
GoMock is a mocking framework for the Go programming language.

gomock GoMock is a mocking framework for the Go programming language. It integrates well with Go's built-in testing package, but can be used in other

Dec 28, 2022
Powerful mock generation tool for Go programming language

Summary Minimock generates mocks out of Go interface declarations. The main features of minimock are: It generates statically typed mocks and helpers.

Dec 17, 2022
Coverage testing tool for The Go Programming Language

gocov Coverage reporting tool for The Go Programming Language Installation go get github.com/axw/gocov/gocov Usage There are currently four gocov comm

Jan 3, 2023
🏳️ Go package that provides function like assert of Python or C++.

gassert gassert is Go package that provides function like assert of Python or C++. With gassert, you can check validation of parameters or values more

Aug 5, 2022
Toy-redis-mock - Experimentation with parsing the redis wire protocol from scratch

Overview Simple app for practicing implementing server-side Redis wire protocol

Jan 9, 2022
Random is a package written in Go that implements pseudo-random number generators for various distributions.

Random This package implements pseudo-random number generators for various distributions. For integers, there is a function for random selection from

Jul 11, 2022
A Master list of Go Programming Tutorials, their write-ups, their source code and their current build status!
A Master list of Go Programming Tutorials, their write-ups, their source code and their current build status!

TutorialEdge TutorialEdge.net Go Tutorials ??‍?? ??‍?? Welcome to the TutorialEdge Go Repository! The goal of this repo is to be able to keep track of

Dec 18, 2022
Assert to perform programming assertions

Cli EN README Assert para realizar las aserciones de programación. GoDoc godoc for github Menú Principal Se configura un menú con ese ejemplo como dis

Jan 13, 2022
A next-generation testing tool. Orion provides a powerful DSL to write and automate your acceptance tests

Orion is born to change the way we implement our acceptance tests. It takes advantage of HCL from Hashicorp t o provide a simple DSL to write the acceptance tests.

Aug 31, 2022
testtime provides time.Now for testing.

testtime provides time.Now for testing.

Dec 21, 2022