Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] feature/SCION HTTP endpoints and Webview #167

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions _examples/shttp/proxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func main() {
// parseUDPAddr validates if the address is a SCION address
// which we can use to proxy to SCION
if _, err := snet.ParseUDPAddr(*remote); err == nil {
proxyHandler, err := shttp.NewSingleSCIONHostReverseProxy(*remote, &tls.Config{InsecureSkipVerify: true})
proxyHandler, err := shttp.NewSingleSCIONHostReverseProxy(*remote, &tls.Config{InsecureSkipVerify: true, NextProtos: []string{"H3-22"}})
if err != nil {
log.Fatalf("Failed to create SCION reverse proxy %s", err)
}
Expand All @@ -50,5 +50,4 @@ func main() {
log.Printf("Listen on HTTP %s\n", *local)
log.Fatalf("%s", http.ListenAndServe(*local, mux))
}

}
2 changes: 1 addition & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -384,4 +384,4 @@ rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
zombiezen.com/go/capnproto2 v0.0.0-20190813022230-ddfb9bb855fa h1:oaUsHxHA/DGY+XtWA4i5FLMxwWheqW21Az6xSLS1OzY=
zombiezen.com/go/capnproto2 v0.0.0-20190813022230-ddfb9bb855fa/go.mod h1:lwDtzVwsLXcHGddc2q1vAWPfocWiFh07Mmeqo/Oatko=
zombiezen.com/go/capnproto2 v0.0.0-20190813022230-ddfb9bb855fa/go.mod h1:lwDtzVwsLXcHGddc2q1vAWPfocWiFh07Mmeqo/Oatko=
34 changes: 34 additions & 0 deletions webapp/web/static/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,37 @@ img {
[tooltip]:hover:before, [tooltip]:hover:after {
display: block;
}

.card {
/* Add shadows to create the "card" effect */
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
max-width: 300px;
text-align: center;
}

/* On mouse-over, add a deeper shadow */
.card:hover {
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}

/* Add some padding inside the card container */
.text-container {
padding: 2px 16px;
}

.cards {
display: flex;
}

.link {
cursor: pointer;
}

.link:hover {
transform: scale(1.1);
}

#tabs {
padding: 1em;
}
1 change: 1 addition & 0 deletions webapp/web/template/header.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
<li><a id="nav-trc" href="/trc">Trust</a></li>
<li><a id="nav-files" href="/files">Files</a></li>
<li><a id="nav-about" href="/about">About</a></li>
<li><a id="nav-webview" href="/webview">SCION HTTP</a></li>
</ul>
</div>
</div>
Expand Down
105 changes: 105 additions & 0 deletions webapp/web/template/webview.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
{{define "webview"}} {{template "header" .}}

<div class="content">

<h2>SCION HTTP</h2>
This page provides some default SCION HTTP endpoints and a searchbar for SCION HTTP URLs, to provide easy access to those resources.
<br />
<br />
<button id="webviewload" onclick="back()">Home</button>
<input id="webviewinput" placeholder="https://19:1:ffaa[127.0.0.1]:8000" style="width: 800px">
<button id="webviewload" onclick="loadSCIONUrl()">GO</button>
</div>

<iframe id="webview" src="about:blank" width="1600" height="900" frameborder="0" style="display: none;"></iframe>
<div id="tabs" class="sb-default-tabs" style="display: block;">
<h3>Trending:</h3>
<div class="cards">
<div class="card">
<img src="https://docs.scionlab.org/favicon.ico" alt="Avatar" style="width:100%">
<div class="text-container">
<h4><b>DVB-T2 Live Stream</b></h4>
<p class="link" onclick="openLiveStream('https://19-ffaa:1:bcc,[127.0.0.1]:9001/bysid/769', 'DVB-T2 Live Stream')">Open Stream</p>
</div>
</div>
</div>
</div>
<script>
showTabs = true;
function getPosition(string, subString, index) {
return string.split(subString, index).join(subString).length;
}

function back() {
showTabs = true;
$('#webview').hide();
$('#tabs').show();
}

function download(filename, text) {
var element = document.createElement('a');
element.setAttribute('href', 'data:video/mpeg;charset=utf-8,' + encodeURIComponent(text));
// element.setAttribute('download', filename);

element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}

function openLiveStream(remote, title) {

var sRemotesIndex = getPosition(remote, '/', 3);
var sRemote = remote.substring(0, sRemotesIndex);
var sContext = remote.substring(sRemotesIndex, remote.length);

var str="#EXTM3U\n#EXTINF:5000,$title\n$target";
var target = 'http://' + window.location.hostname + ':8000/__proxy' + sContext;

str = str.replace('$title', title);
str = str.replace('$target', target);

$.ajax({
url : 'http://' + window.location.hostname + ':8000/__proxy/setconfig',
type : 'post',
dataType : "text",
data : JSON.stringify({
"remote" : sRemote.replace('https://','')
}),
timeout : 10000,
success : function(data, textStatus, jqXHR) {
download('stream.m3u', str);
},
error : function(jqXHR, textStatus, errorThrown) {
console.error(this.url + ' ' + textStatus + ': ' + errorThrown);
},
});
}

function loadSCIONUrl(target) {
var remote = target || $('#webviewinput').val();
var sRemotesIndex = getPosition(remote, '/', 3);
var sRemote = remote.substring(0, sRemotesIndex);
var sContext = remote.substring(sRemotesIndex, remote.length);
showTabs = false;
$.ajax({
url : 'http://' + window.location.hostname + ':8000/__proxy/setconfig',
type : 'post',
dataType : "text",
data : JSON.stringify({
"remote" : sRemote.replace('https://','')
}),
timeout : 10000,
success : function(data, textStatus, jqXHR) {
$('#webview').show();
$('#tabs').hide();
document.getElementById('webview').src = 'http://' + window.location.hostname + ':8000/__proxy/' + sContext; //remote;
},
error : function(jqXHR, textStatus, errorThrown) {
console.error(this.url + ' ' + textStatus + ': ' + errorThrown);
},
});
}
</script>

{{template "footer" .}} {{end}}
62 changes: 62 additions & 0 deletions webapp/webapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ package main

import (
"bufio"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"html/template"
"io"
"net/http"
"net/http/httputil"
"net/url"
"os"
"os/exec"
"path"
Expand All @@ -38,6 +41,9 @@ import (
lib "github.com/netsec-ethz/scion-apps/webapp/lib"
model "github.com/netsec-ethz/scion-apps/webapp/models"
. "github.com/netsec-ethz/scion-apps/webapp/util"

"github.com/netsec-ethz/scion-apps/pkg/shttp"
"github.com/scionproto/scion/go/lib/snet"
)

var settings lib.UserSetting
Expand Down Expand Up @@ -82,6 +88,53 @@ type Page struct {

var options lib.CmdOptions

var proxy *httputil.ReverseProxy

// Can be overwritte by api calls
type ProxyConfig struct {
Remote string `json:"remote"`
}

//TODO: Thread safe/Routine safe
func setProxyConfig(wr http.ResponseWriter, r *http.Request) {
var newConfig ProxyConfig

// Try to decode the request body into the struct. If there is an error,
// respond to the client with the error message and a 400 status code.
err := json.NewDecoder(r.Body).Decode(&newConfig)
if err != nil {
http.Error(wr, err.Error(), http.StatusBadRequest)
return
}

setRemote(&newConfig.Remote)
}

func proxyWrapper(rw http.ResponseWriter, req *http.Request) {
req.URL.Path = strings.Replace(req.URL.Path, "__proxy/", "", 1)
proxy.ServeHTTP(rw, req)
}

func setRemote(remote *string) {
// parseUDPAddr validates if the address is a SCION address
// which we can use to proxy to SCION
if _, err := snet.ParseUDPAddr(*remote); err == nil {
proxy, err = shttp.NewSingleSCIONHostReverseProxy(*remote, &tls.Config{InsecureSkipVerify: true, NextProtos: []string{"H3-22"}})
if err != nil {
log.Crit("Failed to create SCION reverse proxy %s", err)
}

fmt.Printf("Proxy to SCION remote %s\n", *remote)
} else {
u, err := url.Parse(*remote)
if err != nil {
log.Crit(fmt.Sprintf("Failed parse remote %s, %s", *remote, err))
}
fmt.Printf("Proxy to HTTP remote %s\n", *remote)
proxy = httputil.NewSingleHostReverseProxy(u)
}
}

func ensurePath(srcpath, staticDir string) string {
dir := path.Join(srcpath, staticDir)
if _, err := os.Stat(dir); os.IsNotExist(err) {
Expand Down Expand Up @@ -188,6 +241,7 @@ func initServeHandlers() {
http.HandleFunc("/", mainHandler)
http.HandleFunc("/about", aboutHandler)
http.HandleFunc("/apps", appsHandler)
http.HandleFunc("/webview", webviewHandler)
http.HandleFunc("/astopo", astopoHandler)
http.HandleFunc("/trc", trcHandler)
fsStatic := http.FileServer(http.Dir(path.Join(options.StaticRoot, "static")))
Expand Down Expand Up @@ -217,6 +271,9 @@ func initServeHandlers() {
http.HandleFunc("/getpathtopo", getPathInfoHandler)
http.HandleFunc("/getastopo", getAsTopoHandler)
http.HandleFunc("/gettrc", getTrcInfoHandler)

http.HandleFunc("/__proxy/setconfig", setProxyConfig)
http.HandleFunc("/__proxy/", proxyWrapper)
}

func logRequestHandler(handler http.Handler) http.Handler {
Expand All @@ -237,6 +294,7 @@ func prepareTemplates(srcpath string) *template.Template {
path.Join(srcpath, "template/about.html"),
path.Join(srcpath, "template/astopo.html"),
path.Join(srcpath, "template/trc.html"),
path.Join(srcpath, "template/webview.html"),
))
}

Expand Down Expand Up @@ -275,6 +333,10 @@ func appsHandler(w http.ResponseWriter, r *http.Request) {
display(w, "apps", &Page{Title: "SCIONLab Apps", MyIA: settings.MyIA})
}

func webviewHandler(w http.ResponseWriter, r *http.Request) {
display(w, "webview", &Page{Title: "SCION HTTP", MyIA: settings.MyIA})
}

func astopoHandler(w http.ResponseWriter, r *http.Request) {
display(w, "astopo", &Page{Title: "SCIONLab AS Topology", MyIA: settings.MyIA})
}
Expand Down