Version 3 of multipart/x-mixed-replace

Updated 2021-08-24 08:50:27 by dbohdan

multipart/x-mixed-replace is an HTTP header. Your server can use it to push dynamically updated content to the web browser. It works by telling the browser to keep the connection open and replace the web page or piece of media it is displaying with another when it receives a special token. The header is old and widely supported. It works in legacy browsers, even Netscape 1.1 .

This page shows an implementation of a multipart/x-mixed-replace HTTP server in plain Tcl.

Code

#! /usr/bin/env tclsh
# An example of how to serve dynamic web content with
# multipart/x-mixed-replace.
# Copyright (c) 2021 D. Bohdan.  License: MIT.

package require Tcl 8.6-10

set image [join {
    data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgBAMAAACBVGfHAAAAMFBM
    VEXy9PH+/xb/ysn/ev8A+vmhk/+XmJYA6wCBgYD/R0UArQA1ZiodCuEAMQAAAXEFCARuxRLHAA
    AAg0lEQVR42r3OR1UFURAE0DuFAhz0eQZIGpCAOL4EsDAWMEB2MArI6Z3TK1Z/eztULW/YYXtk
    9focAAURYPKEgATQHFQHHYDl6oYdBqm28QpxDADEQQcd5s1/e7zhHCsu9Y29wXL3bBu4xgWSYr
    STQ6NMCMQZIBSMX0kJ8CMJYwpRAMA7X8kSzYsnKHwAAAAASUVORK5CYII=
} {}]

proc main {} {
    set server [socket -server serve 8080]

    vwait forever
}

proc serve {conn clientAddr clientPort} {
    fconfigure $conn -buffering none -translation binary

    set boundary boundary-[expr rand()]
    send [mp-header $boundary]
    send --$boundary

    send "Content-type: text/plain\r\n"
    send {Hello,       text       }
    send --$boundary
    after 1000

    send "Content-type: text/plain\r\n"
    send {       plain      world!}
    send --$boundary
    after 1000

    send "Content-type: image/png\r\n"
    set imageBin [binary decode base64 [string range $::image 22 end]]
    send $imageBin false
    send --$boundary
    after 1000

    set s {Hello, the <i>wonderful</i> world of HTML!}
    set len [llength $s]
    for {set i -1} {$i <= $len} {incr i} {
        send "Content-type: text/html\r\n"
        send "<!doctype html><h1>[lrange $s 0 $i]</h1>"
        send [expr {
            $i == $len
            ? "<img src=\"$::image\">--$boundary--"
            : "--$boundary"
        }]
        after 300
    }
}

proc send {data {nl true}} {
    upvar 1 conn conn

    puts -nonewline $conn $data
    if {$nl} {
        puts -nonewline $conn \r\n
    }
}

proc mp-header boundary {
    append h "HTTP/1.1 200 OK\r\n"
    append h "Content-type:\
        multipart/x-mixed-replace;boundary=\"$boundary\"\r\n"
    append h \r\n
}

main

See also