22 Commits
0.1 ... 0.2

Author SHA1 Message Date
Antoni Sawicki
fb4848d235 added makefile 2019-06-04 01:42:22 -07:00
Antoni Sawicki
06317022a6 better scaling 2019-06-04 01:30:00 -07:00
Antoni Sawicki
69d4b39eff restored context 2019-06-04 01:23:46 -07:00
Antoni Sawicki
5f6a1154df single line toolbar 2019-06-04 01:20:41 -07:00
Antoni Sawicki
d6005b52fd removed halt server 2019-06-04 01:20:19 -07:00
Antoni Sawicki
fabcd721c3 obtain server address from context, -l no longer needed 2019-06-04 00:58:45 -07:00
Antoni Sawicki
0ee45139c3 readme update 2019-06-03 17:52:02 -07:00
Antoni Sawicki
936cb97bc0 added headed mode and chromedp debug output 2019-06-03 17:50:16 -07:00
Antoni Sawicki
02758bd039 readme update 2019-06-03 01:47:44 -07:00
Antoni Sawicki
a05a30c26f hack for ISMAP redirect 2019-06-03 00:12:17 -07:00
Antoni Sawicki
9c96a62816 dont crash on missing image or map 2019-06-02 22:23:41 -07:00
Antoni Sawicki
5dd4b5feab img border=0 2019-06-02 17:07:31 -07:00
Antoni Sawicki
791e87d7ed added number of colors 2019-06-02 17:06:41 -07:00
Antoni Sawicki
a8cc1b6b4e version branding etc 2019-06-02 16:24:46 -07:00
Antoni Sawicki
9358691ce5 version string 2019-06-02 16:19:08 -07:00
Antoni Sawicki
253d36e963 better handling of ismap variable 2019-06-02 16:18:53 -07:00
Antoni Sawicki
719a7fc560 better logging 2019-06-02 16:05:36 -07:00
Antoni Sawicki
3270bbcdd3 ismap server cruft 2019-06-02 15:55:58 -07:00
Antoni Sawicki
6dfe7ddafc added more ismap cruft 2019-06-02 02:25:18 -07:00
Antoni Sawicki
a6df4cbec4 readme update 2019-06-01 01:26:18 -07:00
Antoni Sawicki
e48f0c9ff2 formatting 2019-06-01 01:17:50 -07:00
Antoni Sawicki
c7fcea908f changed names to prev/next due to issue with ie1.5 2019-06-01 01:17:41 -07:00
4 changed files with 190 additions and 72 deletions

22
Makefile Normal file
View File

@@ -0,0 +1,22 @@
all: linux freebsd openbsd macos windows rpi
linux:
GOOS=linux GOARCH=amd64 go build -a -o wrp-linux wrp.go
freebsd:
GOOS=freebsd GOARCH=amd64 go build -a -o wrp-freebsd wrp.go
openbsd:
GOOS=openbsd GOARCH=amd64 go build -a -o wrp-openbsd wrp.go
macos:
GOOS=darwin GOARCH=amd64 go build -a -o wrp-macos wrp.go
windows:
GOOS=windows GOARCH=amd64 go build -a -o wrp-windows.exe wrp.go
rpi:
GOOS=linux GOARCH=arm go build -a -o wrp-linux-rpi wrp.go
clean:
rm -rf wrp-linux wrp-freebsd wrp-openbsd wrp-macos wrp-windows.exe wrp-linux-rpi

View File

@@ -11,22 +11,21 @@ A HTTP proxy server that allows to use historical and obsolete web browsers on t
* basic browser-in-browser mode
* screenshot and serve image+map via CDP
* gif with FloydSteinberg dithering
* random image addressing
* multiple concurent client support
* resolve relative links
* paginated scrolling
* google search on input not starting with ^http
* ISMAP, although for a redirect to work `-i` flag must be specified
otherwise http-equiv refresh will be used and/or link provided
* headed mode and chromedp debug output
## Todo
* ISMAP - underway
* net/url: invalid control character in URL on Windows
* configurable color palete and quantization
* real http proxy support
* option to encode as png/jpeg
* padded box model coordinates
* better http server shutdown
* chromedp logging, timeout, non-headless flags
## Python version
## Old Python version
Check [master branch](https://github.com/tenox7/wrp/tree/master) for "stable" Python-Webkit version.
Check [pywebkit/](/pywebkit) folder for the old Python-Webkit version.

29
pywebkit/README.md Normal file
View File

@@ -0,0 +1,29 @@
# WRP - Web Rendering Proxy
A HTTP proxy server that allows to use historical and obsolete web browsers on the modern web. It works by rendering the web page in to a GIF/PNG/JPEG image associated with clickable imagemap of original web links.
# Current Status
* This is a WebKit / Python version of WRP.
* No longer maintained / supported.
* You should be using GoLang/CDP version instead.
* It mostly works for casual browsing but it's not very stable.
* Secure aka https/SSL/TLS websites might work with use of [sslstrip](https://moxie.org/software/sslstrip/) cheat (enabled by default).
## OS Support
WRP works on macOS (Mac OS X), Linux and FreeBSD. On macOS it uses Cocoa Webkit, on Linux/FreeBSD QT Webkit, for which needs PyQT4 or PyQT5. It does not work on Windows. Use Go/CDP version for that.
## Installation
* macOS - should just work
* Linux/FreeBSD install `python-pyqt5.qtwebkit` and `sslstrip`
* For PythonMagick (Imagemagick library) install `python-pythonmagick`
## Configuration
Edit wrp.py, scroll past Copyright section to find config parameters
## Usage
Configure your web browser to use HTTP proxy at IP address and port where WRP is running. If using browsers prior to HTML 3.2, ISMAP option may need to be enabled. Check configuration.
## More info and screenshots
* http://virtuallyfun.superglobalmegacorp.com/2014/03/11/web-rendering-proxy-update/
* http://virtuallyfun.superglobalmegacorp.com/2014/03/03/surfing-modern-web-with-ancient-browsers/

198
wrp.go
View File

@@ -19,7 +19,6 @@ import (
"math/rand"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"
@@ -32,72 +31,96 @@ import (
"github.com/chromedp/chromedp"
)
// Ismap for server side processing
type Ismap struct {
xmin int64
ymin int64
xmax int64
ymax int64
url string
}
var (
ctx context.Context
cancel context.CancelFunc
gifmap = make(map[string]bytes.Buffer)
version = "3.0"
ctx context.Context
cancel context.CancelFunc
gifmap = make(map[string]bytes.Buffer)
ismap = make(map[string][]Ismap)
)
func pageServer(out http.ResponseWriter, r *http.Request) {
r.ParseForm()
u := r.FormValue("url")
func pageServer(out http.ResponseWriter, req *http.Request) {
req.ParseForm()
u := req.FormValue("url")
var istr string
var ion string
var i bool
if r.FormValue("i") == "on" {
if req.FormValue("i") == "on" {
istr = "CHECKED"
i = true
ion = "&i=on"
} else {
istr = ""
i = false
}
p, _ := strconv.ParseInt(r.FormValue("p"), 10, 64)
if r.FormValue("pg") == ">>" {
p, _ := strconv.ParseInt(req.FormValue("p"), 10, 64)
if req.FormValue("pg") == "Dn" {
p++
} else if r.FormValue("pg") == "<<" {
} else if req.FormValue("pg") == "Up" {
p--
} else {
p = 0
}
w, _ := strconv.ParseInt(r.FormValue("w"), 10, 64)
w, _ := strconv.ParseInt(req.FormValue("w"), 10, 64)
if w < 10 {
w = 1024
}
h, _ := strconv.ParseInt(r.FormValue("h"), 10, 64)
h, _ := strconv.ParseInt(req.FormValue("h"), 10, 64)
if h < 10 {
h = 768
}
s, _ := strconv.ParseFloat(r.FormValue("s"), 64)
s, _ := strconv.ParseFloat(req.FormValue("s"), 64)
if s < 0.1 {
s = 1.0
}
log.Printf("%s Page Reqest for url=\"%s\" [%s]\n", r.RemoteAddr, u, r.URL.Path)
c, _ := strconv.ParseInt(req.FormValue("c"), 10, 64)
if c < 2 || c > 256 {
c = 256
}
log.Printf("%s Page Reqest for url=\"%s\" [%s]\n", req.RemoteAddr, u, req.URL.Path)
out.Header().Set("Content-Type", "text/html")
fmt.Fprintf(out, "<HTML>\n<HEAD><TITLE>WRP %s</TITLE></HEAD>\n<BODY BGCOLOR=\"#F0F0F0\">", u)
fmt.Fprintf(out, "<FORM ACTION=\"/\">URL/Search: <INPUT TYPE=\"TEXT\" NAME=\"url\" VALUE=\"%s\" SIZE=\"40\">", u)
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" VALUE=\"Go\"><P>\n")
fmt.Fprintf(out, "ISMAP:<INPUT TYPE=\"CHECKBOX\" NAME=\"i\" %s> \n", istr)
fmt.Fprintf(out, "Width:<INPUT TYPE=\"TEXT\" NAME=\"w\" VALUE=\"%d\" SIZE=\"4\"> \n", w)
fmt.Fprintf(out, "Height:<INPUT TYPE=\"TEXT\" NAME=\"h\" VALUE=\"%d\" SIZE=\"4\"> \n", h)
fmt.Fprintf(out, "Scale:<INPUT TYPE=\"TEXT\" NAME=\"s\" VALUE=\"%1.2f\" SIZE=\"3\"> \n", s)
fmt.Fprintf(out, "Page:<INPUT TYPE=\"HIDDEN\" NAME=\"p\" VALUE=\"%d\"> \n", p)
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" NAME=\"pg\" VALUE=\"<<\"> %d \n", p)
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" NAME=\"pg\" VALUE=\">>\"> \n")
fmt.Fprintf(out, "</FORM><P>\n")
if len(u) > 4 {
fmt.Fprintf(out, "<!-- Web Rendering Proxy Version %s -->\n", version)
fmt.Fprintf(out, "<HTML>\n<HEAD><TITLE>WRP %s</TITLE></HEAD>\n<BODY BGCOLOR=\"#F0F0F0\">\n", u)
fmt.Fprintf(out, "<FORM ACTION=\"/\"><INPUT TYPE=\"TEXT\" NAME=\"url\" VALUE=\"%s\" SIZE=\"20\">", u)
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" VALUE=\"Go\"> \n")
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" NAME=\"pg\" VALUE=\"Up\"> \n")
fmt.Fprintf(out, "<INPUT TYPE=\"TEXT\" NAME=\"p\" VALUE=\"%d\" SIZE=\"2\"> \n", p)
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" NAME=\"pg\" VALUE=\"Dn\"> \n")
fmt.Fprintf(out, "I <INPUT TYPE=\"CHECKBOX\" NAME=\"i\" %s> \n", istr)
fmt.Fprintf(out, "W <INPUT TYPE=\"TEXT\" NAME=\"w\" VALUE=\"%d\" SIZE=\"4\"> \n", w)
fmt.Fprintf(out, "H <INPUT TYPE=\"TEXT\" NAME=\"h\" VALUE=\"%d\" SIZE=\"4\"> \n", h)
fmt.Fprintf(out, "S <INPUT TYPE=\"TEXT\" NAME=\"s\" VALUE=\"%1.2f\" SIZE=\"3\"> \n", s)
fmt.Fprintf(out, "C <INPUT TYPE=\"TEXT\" NAME=\"c\" VALUE=\"%d\" SIZE=\"3\"> \n", c)
fmt.Fprintf(out, "</FORM><BR>\n")
if len(u) > 1 {
if strings.HasPrefix(u, "http") {
capture(u, w, h, s, p, i, out)
capture(u, w, h, s, int(c), p, i, req.RemoteAddr, out)
} else {
capture(fmt.Sprintf("http://www.google.com/search?q=%s", url.QueryEscape(u)), w, h, s, p, i, out)
capture(fmt.Sprintf("http://www.google.com/search?q=%s", url.QueryEscape(u)), w, h, s, int(c), p, i, req.RemoteAddr, out)
}
} else {
fmt.Fprintf(out, "No URL or search query specified")
}
fmt.Fprintf(out, "</BODY>\n</HTML>\n")
fmt.Fprintf(out, "\n<P><A HREF=\"/?url=https://github.com/tenox7/wrp/&w=%d&h=%d&s=%1.2f&c=%d%s\">Web Rendering Proxy Version %s</A></BODY>\n</HTML>\n", w, h, s, c, ion, version)
}
func imgServer(out http.ResponseWriter, req *http.Request) {
log.Printf("%s IMG Request for %s\n", req.RemoteAddr, req.URL.Path)
gifbuf := gifmap[req.URL.Path]
gifbuf, ok := gifmap[req.URL.Path]
if !ok || gifbuf.Bytes() == nil {
fmt.Fprintf(out, "Unable to find image %s\n", req.URL.Path)
log.Printf("Unable to find image %s\n", req.URL.Path)
return
}
defer delete(gifmap, req.URL.Path)
out.Header().Set("Content-Type", "image/gif")
out.Header().Set("Content-Length", strconv.Itoa(len(gifbuf.Bytes())))
@@ -106,32 +129,49 @@ func imgServer(out http.ResponseWriter, req *http.Request) {
}
func mapServer(out http.ResponseWriter, req *http.Request) {
log.Printf("%s MAP Request for %s [%v]\n", req.RemoteAddr, req.URL.Path, req.URL.Query())
log.Printf("%s ISMAP Request for %s [%+v]\n", req.RemoteAddr, req.URL.Path, req.URL.RawQuery)
var loc string
var x, y int64
n, err := fmt.Sscanf(req.URL.RawQuery, "%d,%d", &x, &y)
if err != nil || n != 2 {
fmt.Fprintf(out, "n=%d, err=%s\n", n, err)
log.Printf("%s ISMAP n=%d, err=%s\n", req.RemoteAddr, n, err)
return
}
is, ok := ismap[req.URL.Path]
if !ok || is == nil {
fmt.Fprintf(out, "Unable to find map %s\n", req.URL.Path)
log.Printf("Unable to find map %s\n", req.URL.Path)
return
}
defer delete(ismap, req.URL.Path)
for _, i := range is {
if x >= i.xmin && x <= i.xmax && y >= i.ymin && y <= i.ymax {
loc = i.url
}
}
if len(loc) < 1 {
loc = is[0].url
}
log.Printf("%s ISMAP Redirect to: http://%s%s\n", req.RemoteAddr, req.Context().Value(http.LocalAddrContextKey), loc)
http.Redirect(out, req, fmt.Sprintf("http://%s%s", req.Context().Value(http.LocalAddrContextKey), loc), 301)
}
func haltServer(out http.ResponseWriter, req *http.Request) {
log.Printf("%s Shutdown request received [%s]\n", req.RemoteAddr, req.URL.Path)
out.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(out, "WRP Shutdown")
out.(http.Flusher).Flush()
cancel()
os.Exit(0)
}
func capture(gourl string, w int64, h int64, s float64, p int64, ismap bool, out http.ResponseWriter) {
func capture(gourl string, w int64, h int64, s float64, co int, p int64, i bool, c string, out http.ResponseWriter) {
var nodes []*cdp.Node
ctxx := chromedp.FromContext(ctx)
var pngbuf []byte
var gifbuf bytes.Buffer
var loc string
var res *runtime.RemoteObject
is := make([]Ismap, 0)
var ion string
log.Printf("Processing Caputure Request for %s\n", gourl)
log.Printf("%s Processing Caputure Request for %s\n", c, gourl)
// Run ChromeDP Magic
err := chromedp.Run(ctx,
emulation.SetDeviceMetricsOverride(w, h, s, false),
emulation.SetDeviceMetricsOverride(int64(float64(w)/s), int64(float64(h)/s), s, false),
chromedp.Navigate(gourl),
chromedp.Evaluate(fmt.Sprintf("window.scrollTo(0, %d);", p*int64(float64(h)*float64(0.9))), &res),
chromedp.Sleep(time.Second*1),
@@ -140,38 +180,42 @@ func capture(gourl string, w int64, h int64, s float64, p int64, ismap bool, out
chromedp.Nodes("a", &nodes, chromedp.ByQueryAll))
if err != nil {
log.Printf("%s", err)
log.Printf("%s %s", c, err)
fmt.Fprintf(out, "<BR>%s<BR>", err)
return
}
log.Printf("Landed on: %s, Nodes: %d\n", loc, len(nodes))
log.Printf("%s Landed on: %s, Nodes: %d\n", c, loc, len(nodes))
// Process Screenshot Image
bytes.NewReader(pngbuf).Seek(0, 0)
img, err := png.Decode(bytes.NewReader(pngbuf))
if err != nil {
log.Printf("Failed to decode screenshot: %s\n", err)
log.Printf("%s Failed to decode screenshot: %s\n", c, err)
fmt.Fprintf(out, "<BR>Unable to decode page screenshot:<BR>%s<BR>\n", err)
return
}
gifbuf.Reset()
err = gif.Encode(&gifbuf, img, nil)
err = gif.Encode(&gifbuf, img, &gif.Options{NumColors: co})
if err != nil {
log.Printf("Failed to encode GIF: %s\n", err)
log.Printf("%s Failed to encode GIF: %s\n", c, err)
fmt.Fprintf(out, "<BR>Unable to encode GIF:<BR>%s<BR>\n", err)
return
}
imgpath := fmt.Sprintf("/img/%04d.gif", rand.Intn(9999))
log.Printf("Encoded GIF image: %s, Size: %dKB\n", imgpath, len(gifbuf.Bytes())/1024)
seq := rand.Intn(9999)
imgpath := fmt.Sprintf("/img/%04d.gif", seq)
mappath := fmt.Sprintf("/map/%04d.map", seq)
log.Printf("%s Encoded GIF image: %s, Size: %dKB\n", c, imgpath, len(gifbuf.Bytes())/1024)
gifmap[imgpath] = gifbuf
// Process Nodes
base, _ := url.Parse(loc)
if ismap {
fmt.Fprintf(out, "<A HREF=\"/map/123.map\"><IMG SRC=\"%s\" ALT=\"wrp\" ISMAP></A>", imgpath)
if i {
fmt.Fprintf(out, "<A HREF=\"%s\"><IMG SRC=\"%s\" ALT=\"wrp\" BORDER=\"0\" ISMAP></A>", mappath, imgpath)
is = append(is, Ismap{xmin: -1, xmax: -1, ymin: -1, ymax: -1, url: fmt.Sprintf("/?url=%s&w=%d&h=%d&s=%1.2f&c=%d&i=on", loc, w, h, s, co)})
ion = "&i=on"
} else {
fmt.Fprintf(out, "<IMG SRC=\"%s\" ALT=\"wrp\" USEMAP=\"#map\">\n<MAP NAME=\"map\">\n", imgpath)
fmt.Fprintf(out, "<IMG SRC=\"%s\" ALT=\"wrp\" BORDER=\"0\" USEMAP=\"#map\">\n<MAP NAME=\"map\">\n", imgpath)
}
for _, n := range nodes {
@@ -183,37 +227,61 @@ func capture(gourl string, w int64, h int64, s float64, p int64, ismap bool, out
if err != nil {
continue
}
target := fmt.Sprintf("/?url=%s&w=%d&h=%d&s=%1.2f&", tgt, w, h, s) // no page# here
target := fmt.Sprintf("/?url=%s&w=%d&h=%d&s=%1.2f&c=%d%s", tgt, w, h, s, co, ion) // no page# here
if len(b.Content) > 6 && len(target) > 7 {
if ismap {
if i {
is = append(is, Ismap{
xmin: int64(b.Content[0] * s), ymin: int64(b.Content[1] * s),
xmax: int64(b.Content[4] * s), ymax: int64(b.Content[5] * s),
url: target})
} else {
fmt.Fprintf(out, "<AREA SHAPE=\"RECT\" COORDS=\"%.f,%.f,%.f,%.f\" ALT=\"%s\" TITLE=\"%s\" HREF=\"%s\">\n",
b.Content[0]*s, b.Content[1]*s, b.Content[4]*s, b.Content[5]*s, n.AttributeValue("href"), n.AttributeValue("href"), target)
fmt.Fprintf(out, "<AREA SHAPE=\"RECT\" COORDS=\"%.f,%.f,%.f,%.f\" ALT=\"%s\" TITLE=\"%s\" HREF=\"%s\">\n",
b.Content[0]*s, b.Content[1]*s, b.Content[4]*s, b.Content[5]*s, n.AttributeValue("href"), n.AttributeValue("href"), target)
}
}
}
if !ismap {
if i {
log.Printf("%s Encoded ISMAP %s\n", c, mappath)
} else {
fmt.Fprintf(out, "</MAP>\n")
}
out.(http.Flusher).Flush()
log.Printf("Done with caputure for %s\n", gourl)
log.Printf("%s Done with caputure for %s\n", c, gourl)
ismap[mappath] = is
}
func main() {
ctx, cancel = chromedp.NewContext(context.Background())
defer cancel()
var addr string
var head, headless bool
var debug bool
flag.StringVar(&addr, "l", ":8080", "Listen address:port, default :8080")
flag.BoolVar(&head, "h", false, "Headed mode - display browser window")
flag.BoolVar(&debug, "d", false, "Debug ChromeDP")
flag.Parse()
if head {
headless = false
} else {
headless = true
}
opts := append(chromedp.DefaultExecAllocatorOptions[:],
chromedp.Flag("headless", headless),
)
actx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
defer cancel()
if debug {
ctx, cancel = chromedp.NewContext(actx, chromedp.WithDebugf(log.Printf))
} else {
ctx, cancel = chromedp.NewContext(actx)
}
defer cancel()
rand.Seed(time.Now().UnixNano())
http.HandleFunc("/", pageServer)
http.HandleFunc("/img/", imgServer)
http.HandleFunc("/map/", mapServer)
http.HandleFunc("/favicon.ico", http.NotFound)
http.HandleFunc("/halt", haltServer)
log.Printf("Web Rendering Proxy Version %s\n", version)
log.Printf("Starting WRP http server on %s\n", addr)
http.ListenAndServe(addr, nil)
}