implemented compression for HTTP-requested data

This commit is contained in:
Luxferre
2024-11-23 08:11:48 +02:00
parent badc92ea99
commit 203f6a30ca
2 changed files with 93 additions and 61 deletions
+92 -61
View File
@@ -25,6 +25,7 @@ set nodename "tiid"
proc createdb {fname} {
sqlite3 fdb $fname
fdb eval {
PRAGMA journal_mode=WAL;
CREATE TABLE `msg` (`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`msgid` VARCHAR(20) NOT NULL UNIQUE,
`timestamp` INT NOT NULL,
@@ -256,12 +257,25 @@ proc indexpage {} {
# TCP logic here
# compression function
proc repcompress {compfunc data} {
if {$compfunc eq {gzip}} {
return [zlib gzip $data -level 9]
} else {
return [zlib $compfunc $data 9]
}
}
# error report/reply
proc reperror {sock ishttp errmsg} {
proc reperror {sock ishttp errmsg compfunc} {
set errmsg "error: $errmsg\n"
if {$ishttp eq 1} {
set msglen [string length $errmsg]
set hdrs "Content-Type: text/plain;charset=utf-8\r\nContent-Length: $msglen\r\nConnection: close\r\n"
if {$compfunc ne {none}} {
set hdrs [string cat $hdrs "Content-Encoding: $compfunc\r\n"]
set errmsg [repcompress $compfunc $errmsg]
}
puts -nonewline $sock "HTTP/1.0 400 Bad Request\r\n$hdrs\r\n$errmsg"
} else {
puts -nonewline $sock "$errmsg"
@@ -270,10 +284,14 @@ proc reperror {sock ishttp errmsg} {
}
# successful reply with data
proc repdata {sock ishttp data} {
proc repdata {sock ishttp data compfunc} {
if {$ishttp eq 1} {
set msglen [string length $data]
set hdrs "Content-Type: text/plain;charset=utf-8\r\nContent-Length: $msglen\r\nConnection: close\r\n"
if {$compfunc ne {none}} {
set hdrs [string cat $hdrs "Content-Encoding: $compfunc\r\n"]
set data [repcompress $compfunc $data]
}
puts -nonewline $sock "HTTP/1.0 200 OK\r\n$hdrs\r\n$data"
} else {
puts -nonewline $sock $data
@@ -284,7 +302,7 @@ proc repdata {sock ishttp data} {
# path router
# it only must write to the socket, not read from it or close it
# supported paths: /e, /m, /u/e, /u/m, /u/point, /list.txt, /blacklist.txt
proc routepath {dbfile sock ishttp path body} {
proc routepath {dbfile sock ishttp path body compfunc} {
fconfigure $sock -translation binary
set pathparts [split [string trim $path] /]
if {[llength $pathparts] > 1} {
@@ -309,12 +327,12 @@ proc routepath {dbfile sock ishttp path body} {
[validecho $ename] ? $ename : [continue]
}}]
if {[llength $erange] > 0} { # recheck length after validation
repdata $sock $ishttp [indexechos $dbfile $erange 1 $offset $limit]
repdata $sock $ishttp [indexechos $dbfile $erange 1 $offset $limit] $compfunc
} else {
reperror $sock $ishttp "invalid request"
reperror $sock $ishttp "invalid request" $compfunc
}
} else {
reperror $sock $ishttp "invalid request"
reperror $sock $ishttp "invalid request" $compfunc
}
}
m { # validate and shape the message ID list
@@ -322,9 +340,9 @@ proc routepath {dbfile sock ishttp path body} {
[validmsgid $mid] ? $mid : [continue]
}}]
if {[llength $mrange] > 0} { # we have some valid messages
repdata $sock $ishttp [multimsg $dbfile $mrange]
repdata $sock $ishttp [multimsg $dbfile $mrange] $compfunc
} else {
reperror $sock $ishttp "invalid request"
reperror $sock $ishttp "invalid request" $compfunc
}
}
point {
@@ -349,12 +367,12 @@ proc routepath {dbfile sock ishttp path body} {
if {$authstr ne "" && $msgbody ne ""} {
set postres [postmsg $dbfile $authstr $msgbody]
if [string match "msg ok*" $postres] {
repdata $sock $ishttp $postres
repdata $sock $ishttp $postres $compfunc
} else {
reperror $sock $ishttp $postres
reperror $sock $ishttp $postres $compfunc
}
} else {
reperror $sock $ishttp "invalid request"
reperror $sock $ishttp "invalid request" $compfunc
}
}
push {
@@ -369,68 +387,68 @@ proc routepath {dbfile sock ishttp path body} {
set msgbody [decurl [dict get $params upush]]
}
} else {
reperror $sock $ishttp "/u/push is only available over HTTP POST"
reperror $sock $ishttp "/u/push is only available over HTTP POST" $compfunc
return
}
if {$authstr ne "" && $msgbody ne ""} {
set postres [postbundle $dbfile $authstr $msgbody]
if [string match "message saved: ok*" $postres] {
repdata $sock $ishttp $postres
repdata $sock $ishttp $postres $compfunc
} else {
reperror $sock $ishttp $postres
reperror $sock $ishttp $postres $compfunc
}
} else {
reperror $sock $ishttp "invalid request"
reperror $sock $ishttp "invalid request" $compfunc
}
}
default {
reperror $sock $ishttp "invalid request"
reperror $sock $ishttp "invalid request" $compfunc
}
}
} else {
reperror $sock $ishttp "invalid request"
reperror $sock $ishttp "invalid request" $compfunc
}
}
e {
if {[llength $pathparts] > 2} {
set echoname [string trim [lindex $pathparts 2]]
if {[validecho $echoname]} {
repdata $sock $ishttp [indexechos $dbfile [list $echoname] 0 0 0]
repdata $sock $ishttp [indexechos $dbfile [list $echoname] 0 0 0] $compfunc
} else {
reperror $sock $ishttp "invalid request"
reperror $sock $ishttp "invalid request" $compfunc
}
} else {
reperror $sock $ishttp "invalid request"
reperror $sock $ishttp "invalid request" $compfunc
}
}
m {
if {[llength $pathparts] > 2} {
set mid [string trim [lindex $pathparts 2]]
if {[validmsgid $mid]} {
repdata $sock $ishttp [singlemsg $dbfile $mid]
repdata $sock $ishttp [singlemsg $dbfile $mid] $compfunc
} else {
reperror $sock $ishttp "invalid request"
reperror $sock $ishttp "invalid request" $compfunc
}
} else {
reperror $sock $ishttp "invalid request"
reperror $sock $ishttp "invalid request" $compfunc
}
}
list.txt {
repdata $sock $ishttp [listechos $dbfile]
repdata $sock $ishttp [listechos $dbfile] $compfunc
}
blacklist.txt {
repdata $sock $ishttp [blacklisted $dbfile]
repdata $sock $ishttp [blacklisted $dbfile] $compfunc
}
{} {
repdata $sock $ishttp [indexpage]
repdata $sock $ishttp [indexpage] $compfunc
}
default {
reperror $sock $ishttp "invalid request"
reperror $sock $ishttp "invalid request" $compfunc
}
}
} else {
reperror $sock $ishttp "invalid request"
reperror $sock $ishttp "invalid request" $compfunc
}
}
@@ -440,43 +458,56 @@ proc reqhandler {dbfile sock} {
# ignore all invalid requests by closing the connection
if {[gets $sock line] >= 0} {
if {[string match /* $line]} { # bare TCP request (Gopher/Nex)
routepath $dbfile $sock 0 [string trim $line] {}
} elseif {[string match -nocase {GET /*} $line]} { # HTTP GET request
set rparts [split [string trimleft $line]]
if {[llength $rparts] > 1} { # valid GET request
routepath $dbfile $sock 1 [lindex $rparts 1] {}
}
} elseif {[string match -nocase {POST /*} $line]} { # HTTP POST request
set rparts [split [string trimleft $line]]
if {[llength $rparts] > 1} { # valid POST request, read POST headers and data
set hdrread 0
set hdata {}
set pdata {}
while {$hdrread < 1} {
if {[eof $sock] || [catch {gets $sock line}]} {
break
} else {
if {$line eq ""} {
incr hdrread
continue
}
set hparts [split [string trimleft $line] :]
if {[llength $hparts] > 1} {
set hname [string tolower [string trimright [lindex $hparts 0]]]
set hval [string trim [lindex $hparts 1]]
dict set hdata $hname $hval
}
routepath $dbfile $sock 0 [string trim $line] {} none
} else { # HTTP request, read headers
set hdrread 0
set hdata {}
set docomp none
while {$hdrread < 1} {
if {[eof $sock] || [catch {gets $sock hline}]} {
break
} else {
if {$hline eq ""} {
incr hdrread
continue
}
set hparts [split [string trimleft $hline] :]
if {[llength $hparts] > 1} {
set hname [string tolower [string trimright [lindex $hparts 0]]]
set hval [string trim [lindex $hparts 1]]
dict set hdata $hname $hval
}
}
set readlen 0
if {[dict exists $hdata content-length]} {
set readlen [dict get $hdata content-length]
}
if {[dict exists $hdata accept-encoding]} { # detect if we can gzip the output
set enclist [dict get $hdata accept-encoding]
if {[string match -nocase {*gzip*} $enclist]} {
set docomp gzip
} elseif {[string match -nocase {*deflate*} $enclist]} {
set docomp deflate
} elseif {[string match -nocase {*compress*} $enclist]} {
set docomp compress
}
if {$readlen > 0} { # read the data defined by content-length header
fconfigure $sock -translation {binary lf} -buffering none
set pdata [read $sock $readlen]
}
if {[string match -nocase {GET /*} $line]} { # GET request
set rparts [split [string trimleft $line]]
if {[llength $rparts] > 1} { # valid GET request
routepath $dbfile $sock 1 [lindex $rparts 1] {} $docomp
}
} elseif {[string match -nocase {POST /*} $line]} { # POST request
set rparts [split [string trimleft $line]]
if {[llength $rparts] > 1} { # valid POST request, read POST headers and data
set pdata {}
set readlen 0
if {[dict exists $hdata content-length]} {
set readlen [dict get $hdata content-length]
}
if {$readlen > 0} { # read the data defined by content-length header
fconfigure $sock -translation {binary lf} -buffering none
set pdata [read $sock $readlen]
}
routepath $dbfile $sock 1 [lindex $rparts 1] [string trimleft $pdata] $docomp
}
routepath $dbfile $sock 1 [lindex $rparts 1] [string trimleft $pdata]
}
}
}
+1
View File
@@ -203,6 +203,7 @@ proc n2p_id {binmsg} {
proc createdb {fname} {
sqlite3 fdb $fname
fdb eval {
PRAGMA journal_mode=WAL;
CREATE TABLE `msg` (`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`msgid` VARCHAR(20) NOT NULL UNIQUE,
`timestamp` INT NOT NULL,