func main() { manager, err := golongpoll.StartLongpoll(golongpoll.Options{ LoggingEnabled: true, // NOTE: if not defined here, other options have reasonable defaults, // so no need specifying options you don't care about }) if err != nil { log.Fatalf("Failed to create manager: %q", err) } // pump out random events go generateRandomEvents(manager) // Serve our basic example driver webpage http.HandleFunc("/basic", BasicExampleHomepage) // Serve our event subscription web handler http.HandleFunc("/basic/events", manager.SubscriptionHandler) fmt.Println("Serving webpage at http://127.0.0.1:8081/basic") http.ListenAndServe("127.0.0.1:8081", nil) // We'll never get here as long as http.ListenAndServe starts successfully // because it runs until you kill the program (like pressing Control-C) // Buf if you make a stoppable http server, or want to shut down the // internal longpoll manager for other reasons, you can do so via // Shutdown: manager.Shutdown() // Stops the internal goroutine that provides subscription behavior // Again, calling shutdown is a bit silly here since the goroutines will // exit on main() exit. But I wanted to show you that it is possible. }
func main() { manager, err := golongpoll.StartLongpoll(golongpoll.Options{ LoggingEnabled: true, MaxLongpollTimeoutSeconds: 120, MaxEventBufferSize: 100, EventTimeToLiveSeconds: 60 * 2, // Event's stick around for 2 minutes DeleteEventAfterFirstRetrieval: false, }) if err != nil { log.Fatalf("Failed to create manager: %q", err) } // Serve our example driver webpage http.HandleFunc("/advanced", AdvancedExampleHomepage) // Serve handler that generates events http.HandleFunc("/advanced/user/action", getUserActionHandler(manager)) // Serve handler that subscribes to events. http.HandleFunc("/advanced/events", getEventSubscriptionHandler(manager)) // Start webserver fmt.Println("Serving webpage at http://127.0.0.1:8081/advanced") http.ListenAndServe("127.0.0.1:8081", nil) // We'll never get here as long as http.ListenAndServe starts successfully // because it runs until you kill the program (like pressing Control-C) // Buf if you make a stoppable http server, or want to shut down the // internal longpoll manager for other reasons, you can do so via // Shutdown: manager.Shutdown() // Stops the internal goroutine that provides subscription behavior // Again, calling shutdown is a bit silly here since the goroutines will // exit on main() exit. But I wanted to show you that it is possible. }
func CreateProxy(whiteList, blackList []*regexp.Regexp, verbose bool, whiteListUpdates, blackListUpdates chan string) (*goproxy.ProxyHttpServer, error) { // Start longpoll subscription manager longpollManager, lpErr := golongpoll.StartLongpoll( golongpoll.Options{ LoggingEnabled: false, MaxLongpollTimeoutSeconds: 120, MaxEventBufferSize: 1000, EventTimeToLiveSeconds: 240, DeleteEventAfterFirstRetrieval: false, }) if lpErr != nil { log.Fatalf("Error creating longpoll manager: %v", lpErr) } // Create and start control server for controlling proxy behavior ctlServer := controls.NewControlServer(vars.ProxyControlPort, longpollManager.SubscriptionHandler, whiteListUpdates, blackListUpdates) ctlServer.Serve() // Manually allowed/blocked sites: manualWhiteList := make(map[string]bool) manualBlackList := make(map[string]bool) // Create and start our content blocking proxy: proxy := goproxy.NewProxyHttpServer() proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm) proxy.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { // Prevent upgrades to https so we can easily see everything as plain if req.URL.Scheme == "https" { req.URL.Scheme = "http" } urlString := req.URL.String() // Check for any updates to our whitelist/blacklist values checkWhiteBlackListUpdates(manualWhiteList, manualBlackList, whiteListUpdates, blackListUpdates) // Now apply whitelist/blacklist rules: for _, w := range whiteList { if w.MatchString(urlString) { // whitelisted by rules, but was this specific URL blacklisted // by user? if _, ok := manualBlackList[strings.TrimSpace(urlString)]; ok { // stop trying to find whitelist matches log.Printf("user-DENIED whitelisting: %s\n", req.URL) break } else { log.Printf("WHITELISTED: %s\n", req.URL) notifyProxyEvent("Allowed", req, longpollManager) return req, nil } } } // Check if this was explicitly whitelisted by user: if _, ok := manualWhiteList[strings.TrimSpace(urlString)]; ok { // no need to consider blacklists, serve content log.Printf("user-eplicit WHITELISTED: %s\n", req.URL) notifyProxyEvent("Allowed", req, longpollManager) return req, nil } // See if we're manually allowing this page thru on time only if strings.HasSuffix(urlString, vars.ProxyExceptionString) { urlString := urlString[:len(urlString)-len(vars.ProxyExceptionString)] u, uErr := url.Parse(urlString) if uErr == nil { req.URL = u log.Printf("MANUALLY ALLOWED: %s\n", req.URL) notifyProxyEvent("Manually Allowed", req, longpollManager) return req, nil } else { log.Printf("ERROR trying to rewrite URL. Url: %s, Error: %s", urlString, uErr) return req, goproxy.NewResponse(req, goproxy.ContentTypeHtml, http.StatusForbidden, fmt.Sprintf(`<html> <head><title>BAD URL</title></head> <body> <h1>Ehhh.... wut?</h1> <hr /> <h2>Error rewriting URL:</h2> <p style="color: black; font-family: monospace; background: #DDDDDD; padding: 20px;">%s</p> <p>Error:</p> <p style="color: red; font-family: monospace; background: #DDDDDD; padding: 20px;">%s</p> </body> </html>`, urlString, uErr)) } } for _, b := range blackList { if b.MatchString(urlString) { log.Printf("BLACKLISTED: %s\n", req.URL) notifyProxyEvent("Blocked", req, longpollManager) return req, goproxy.NewResponse(req, goproxy.ContentTypeHtml, http.StatusForbidden, fmt.Sprintf(`<html> <head><title>BLOCKED</title></head> <body> <h1>I pity the fool!</h1> <hr /> <h2>Webpage Blocked</h2> <p style="color: black; font-family: monospace; background: #DDDDDD; padding: 20px;">%s</p> <p><a href="%s%s">Continue to Webpage just this once.</a></p> <p>or...</p> <p><a href="http://127.0.0.1:%s/add-wl?url=%s&continue_to_page=yes">Add to Whitelist and continue.</a></p> </body> </html>`, req.URL, req.URL, vars.ProxyExceptionString, vars.ProxyControlPort, url.QueryEscape(req.URL.String()))) } } log.Printf("NOT MATCHED: (allow by default) %s\n", req.URL) notifyProxyEvent("Not matched, default allowed", req, longpollManager) return req, nil }) proxy.OnResponse(goproxy_html.IsHtml).Do(goproxy_html.HandleString( func(s string, ctx *goproxy.ProxyCtx) string { if strings.HasPrefix(ctx.Req.URL.Host, "http://127.0.0.1:") || strings.HasPrefix(ctx.Req.URL.Host, "http://127.0.0.1/") || strings.HasPrefix(ctx.Req.URL.Host, "127.0.0.1/") || strings.HasPrefix(ctx.Req.URL.Host, "127.0.0.1:") { // Don't inject on our own content. // TODO: move this logic next to IsHtml so this func return s } // Don't inject iframe into responses that aren't successful // ie 2xx response codes. // Mainly this is to avoid injecting on our own block page, // but it probably doesn't make sense for other failed pages either if ctx.Resp.StatusCode < 200 || ctx.Resp.StatusCode >= 300 { // show page as-is // remember: blocking content is already enforced by this point, return s } match := vars.StartBodyTagMatcher.FindIndex([]byte(s)) if match != nil && len(match) >= 2 { // TODO: make this more efficient by using a stream or some sort // of stringbuilder like thing that doesn't require mashing // giant strings together. return s[:match[1]] + // TODO: should this script get injected after the iframe to prevent a potential race condition? getParentControlScript() + "<div id=\"proxyblock-glass-overlay\" onclick=\"glassClose(this);\" style=\"position: fixed; top: 0; right: 0; left: 0; bottom: 0; background: #000000; opacity: 0.3; z-index: 99999998; display: none;\"></div>" + "<div id=\"proxyblock-controls\" style=\"position: fixed; height: 42px; width: 230px; top: 4px; right: 8px; z-index: 99999999;\">" + "<iframe id=\"proxyblock-frame\" scrolling=\"no\" style=\"overflow: hidden; background-color: #FFFFFF; border: 2px solid black; width: 100%; height: 100%;\" " + "src=\"http://127.0.0.1:" + vars.ProxyControlPort + pagecontrols.GetPageControlsUrl(ctx.Req.URL.String()) + "\"></iframe>" + "</div>" + s[match[1]:] } else { log.Printf("WARNING: No starting body tag found, must not be html, no injection.") return s } })) proxy.Verbose = verbose return proxy, nil }