Page load and parsing durations in HTTP header

This commit is contained in:
Thomas Buckley-Houston 2018-07-24 15:54:45 +08:00
parent 2f713f40fc
commit 514260205c
9 changed files with 74 additions and 18 deletions

View file

@ -22,7 +22,7 @@ var (
type incomingRawText struct { type incomingRawText struct {
RequestID string `json:"request_id"` RequestID string `json:"request_id"`
RawText string `json:"body"` RawJSON string `json:"json"`
} }
func startWebSocketServer() { func startWebSocketServer() {
@ -96,7 +96,7 @@ func handleRawFrameTextCommands(parts []string) {
} }
if incoming.RequestID != "" { if incoming.RequestID != "" {
Log("Raw text for " + incoming.RequestID) Log("Raw text for " + incoming.RequestID)
rawTextRequests[incoming.RequestID] = incoming.RawText rawTextRequests[incoming.RequestID] = incoming.RawJSON
} else { } else {
Log("Raw text but no associated request ID") Log("Raw text but no associated request ID")
} }

View file

@ -2,6 +2,7 @@ package browsh
import ( import (
"crypto/rand" "crypto/rand"
"encoding/json"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@ -21,6 +22,12 @@ import (
// real browser to render the webpage, we keep track of requests in a map. // real browser to render the webpage, we keep track of requests in a map.
var rawTextRequests = make(map[string]string) var rawTextRequests = make(map[string]string)
type rawTextResponse struct {
PageloadDuration int `json:"page_load_duration"`
ParsingDuration int `json:"parsing_duration"`
Text string `json:"body"`
}
// HTTPServerStart starts the HTTP server is a seperate service from the usual interactive TTY // HTTPServerStart starts the HTTP server is a seperate service from the usual interactive TTY
// app. It accepts normal HTTP requests and uses the path portion of the URL as the entry to the // app. It accepts normal HTTP requests and uses the path portion of the URL as the entry to the
// Browsh URL bar. It then returns a simple line-broken text version of whatever the browser // Browsh URL bar. It then returns a simple line-broken text version of whatever the browser
@ -81,6 +88,7 @@ func (h *slashFix) ServeHTTP(w http.ResponseWriter, r *http.Request) {
func handleHTTPServerRequest(w http.ResponseWriter, r *http.Request) { func handleHTTPServerRequest(w http.ResponseWriter, r *http.Request) {
var message string var message string
var isErrored bool var isErrored bool
var start = time.Now().Format(time.RFC3339)
urlForBrowsh, _ := url.PathUnescape(strings.TrimPrefix(r.URL.Path, "/")) urlForBrowsh, _ := url.PathUnescape(strings.TrimPrefix(r.URL.Path, "/"))
urlForBrowsh, isErrored = deRecurseURL(urlForBrowsh) urlForBrowsh, isErrored = deRecurseURL(urlForBrowsh)
if isErrored { if isErrored {
@ -128,6 +136,7 @@ func handleHTTPServerRequest(w http.ResponseWriter, r *http.Request) {
return return
} }
rawTextRequestID := pseudoUUID() rawTextRequestID := pseudoUUID()
rawTextRequests[rawTextRequestID+"-start"] = start
mode := getRawTextMode(r) mode := getRawTextMode(r)
sendMessageToWebExtension( sendMessageToWebExtension(
"/raw_text_request," + rawTextRequestID + "," + "/raw_text_request," + rawTextRequestID + "," +
@ -198,13 +207,36 @@ func getRawTextMode(r *http.Request) string {
func waitForResponse(rawTextRequestID string, w http.ResponseWriter) { func waitForResponse(rawTextRequestID string, w http.ResponseWriter) {
var rawTextRequestResponse string var rawTextRequestResponse string
var jsonResponse rawTextResponse
var totalTime, pageLoad, parsing string
var ok bool var ok bool
for { for {
if rawTextRequestResponse, ok = rawTextRequests[rawTextRequestID]; ok { if rawTextRequestResponse, ok = rawTextRequests[rawTextRequestID]; ok {
io.WriteString(w, rawTextRequestResponse) jsonResponse = unpackResponse(rawTextRequestResponse)
totalTime = getTotalTiming(rawTextRequests[rawTextRequestID+"-start"])
pageLoad = fmt.Sprintf("%dms", jsonResponse.PageloadDuration)
parsing = fmt.Sprintf("%dms", jsonResponse.ParsingDuration)
w.Header().Set("X-Browsh-Duration-Total", totalTime+"ms")
w.Header().Set("X-Browsh-Duration-Pageload", pageLoad)
w.Header().Set("X-Browsh-Duration-Parsing", parsing)
io.WriteString(w, jsonResponse.Text)
delete(rawTextRequests, rawTextRequestID) delete(rawTextRequests, rawTextRequestID)
break break
} }
time.Sleep(10 * time.Millisecond) time.Sleep(1 * time.Millisecond)
} }
} }
func unpackResponse(jsonString string) rawTextResponse {
var response rawTextResponse
jsonBytes := []byte(jsonString)
if err := json.Unmarshal(jsonBytes, &response); err != nil {
}
return response
}
func getTotalTiming(startString string) string {
start, _ := time.Parse(time.RFC3339, startString)
elapsed := time.Since(start) / time.Millisecond
return fmt.Sprintf("%d", elapsed)
}

View file

@ -158,14 +158,22 @@ export default class extends utils.mixins(CommonMixin, TTYCommandsMixin) {
} }
_applyUpdates(tabish_object) { _applyUpdates(tabish_object) {
let tab = this._maybeNewTab({ id: tabish_object.id }); let tab = this._maybeNewTab({
["id", "title", "url", "active", "request_id", "raw_text_mode_type"].map( id: tabish_object.id
key => { });
if (tabish_object.hasOwnProperty(key)) { [
tab[key] = tabish_object[key]; "id",
} "title",
"url",
"active",
"request_id",
"raw_text_mode_type",
"start_time"
].map(key => {
if (tabish_object.hasOwnProperty(key)) {
tab[key] = tabish_object[key];
} }
); });
if (tabish_object.active) { if (tabish_object.active) {
this.active_tab_id = tab.id; this.active_tab_id = tab.id;
} }
@ -289,7 +297,9 @@ export default class extends utils.mixins(CommonMixin, TTYCommandsMixin) {
} }
} }
}, },
{ urls: ["*://*/*"] }, {
urls: ["*://*/*"]
},
["blocking"] ["blocking"]
); );
} }

View file

@ -120,6 +120,7 @@ export default class extends utils.mixins(CommonMixin, TabCommandsMixin) {
sendGlobalConfig(config) { sendGlobalConfig(config) {
config.http_server_mode_type = this._calculateMode(); config.http_server_mode_type = this._calculateMode();
config.start_time = this.start_time;
this.channel.postMessage(`/config,${JSON.stringify(config)}`); this.channel.postMessage(`/config,${JSON.stringify(config)}`);
} }

View file

@ -47,8 +47,11 @@ export default MixinBase =>
} }
_rawTextRequest(incoming) { _rawTextRequest(incoming) {
incoming.request_id = this.request_id; let payload = {
this.sendToTerminal(`/raw_text,${JSON.stringify(incoming)}`); json: JSON.stringify(incoming),
request_id: this.request_id
};
this.sendToTerminal(`/raw_text,${JSON.stringify(payload)}`);
this._tabCount(count => { this._tabCount(count => {
if (count > 1) { if (count > 1) {
this.remove(); this.remove();

View file

@ -190,7 +190,8 @@ export default MixinBase =>
this._acknowledgeNewTab({ this._acknowledgeNewTab({
id: native_tab.id, id: native_tab.id,
request_id: request_id, request_id: request_id,
raw_text_mode_type: mode.toLowerCase() raw_text_mode_type: mode.toLowerCase(),
start_time: Date.now()
}); });
// Sometimes tabs fail to load for whatever reason. Make sure they get // Sometimes tabs fail to load for whatever reason. Make sure they get
// removed to save RAM in long-lived Browsh HTTP servers // removed to save RAM in long-lived Browsh HTTP servers

View file

@ -37,7 +37,6 @@ export default class extends utils.mixins(CommonMixin, CommandsMixin) {
this.graphics_builder, this.graphics_builder,
this.config this.config
); );
this.text_builder._raw_text_start = performance.now();
} }
_willHideText() { _willHideText() {
@ -204,6 +203,8 @@ export default class extends utils.mixins(CommonMixin, CommandsMixin) {
}); });
window.addEventListener("load", () => { window.addEventListener("load", () => {
this.is_page_finished_loading = true; this.is_page_finished_loading = true;
this.config.page_load_duration = Date.now() - this.config.start_time;
this.text_builder.config = this.config;
this.log("PAGE LOADED"); this.log("PAGE LOADED");
}); });
window.addEventListener("unload", () => { window.addEventListener("unload", () => {

View file

@ -85,8 +85,9 @@ export default MixinBase =>
_getMetaData() { _getMetaData() {
let metadata = ""; let metadata = "";
this._markParsingDuration();
const date_time = this._getCurrentDataTime(); const date_time = this._getCurrentDataTime();
const elapsed = `${performance.now() - this._raw_text_start}ms`; const elapsed = `${this._parsing_duration}ms`;
metadata += metadata +=
"\n\n" + `Built ` + this._byBrowsh() + `on ${date_time} in ${elapsed}.`; "\n\n" + `Built ` + this._byBrowsh() + `on ${date_time} in ${elapsed}.`;
if (this.dimensions.is_page_truncated) { if (this.dimensions.is_page_truncated) {
@ -189,6 +190,10 @@ export default MixinBase =>
} }
} }
_markParsingDuration() {
this._parsing_duration = performance.now() - this._parse_start_time;
}
_getCurrentDataTime() { _getCurrentDataTime() {
let current_date = new Date(); let current_date = new Date();
const offset = -(new Date().getTimezoneOffset() / 60); const offset = -(new Date().getTimezoneOffset() / 60);
@ -293,7 +298,9 @@ export default MixinBase =>
_sendRawText() { _sendRawText() {
let payload = { let payload = {
body: this._serialiseRawText() body: this._serialiseRawText(),
page_load_duration: this.config.page_load_duration,
parsing_duration: this._parsing_duration
}; };
this.sendMessage(`/raw_text,${JSON.stringify(payload)}`); this.sendMessage(`/raw_text,${JSON.stringify(payload)}`);
} }

View file

@ -29,6 +29,7 @@ export default class extends utils.mixins(CommonMixin, SerialiseMixin) {
sendRawText(type) { sendRawText(type) {
this._raw_mode_type = type; this._raw_mode_type = type;
this._parse_start_time = performance.now();
this.buildFormattedText(this._sendRawText.bind(this)); this.buildFormattedText(this._sendRawText.bind(this));
} }