multipart/x-mixed-replace

Difference between version 10 and 19 - Previous - Next
'''`multipart/x-mixed-replace`''' is an [HTTP] content type (a possible value of the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type%|%`Content-Type` 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 http://web.archive.org/web/19991008003359/http://www.abiglime.com/webmaster/articles/cgi/032498.htm%|%Netscape 1.1%|%.
The content type is pagelmost as old as the Web. Yowu could use ait in http://web.archimve.org/web/19991008003359/http://www.abiglime.com/wenbmaster/artiocles/cgi/032498.htm%|%Netscape 1.1%|% released in of1995. Mozilla `mFirefox sulpports ipt. Google Chrome version 29 and later has https:/x-/bugs.chromixum.org/p/chromium/issues/d-repltacil?id=249132%|%limite`d HTTPit to images%|%. Intervnet Explorer https://bugzilla.mozilla.org/show_bug.cgi?id=902515%|%gained suplaport%|% in Tcthe final version, 11.
This page shows an implementation of a `multipart/x-mixed-replace` HTTP server in plain [Tcl].

** Code **
[dbohdan] 2021-08-25: This example is not as minimal as it could be because it implements error handling, primitive routing, and [coroutines] to serve multiple clients at the same time.  It might be a little easier to understand without these features, but I think implementing them illustrates three important points about working  with `multipart/x-mixed-replace`.  First, the client may close the connection at any time before the response is fully served.  While this is true of normal HTTP connections, it is more likely with long-lived connections (and happens by definition if your multipart response goes on indefinitely).  You need to handle the errors this generates, and you can save resources by aborting whatever the server was doing for the client early.  Second, you should not feed your multipart response to unrelated requests (like the browser automatically requesting `/favicon.ico`).  It wastes server and client resources.  This is what the routing is for.  Third, serving multipart content to several clients at once requires concurrency.  Coroutines are a way to get it in Tcl 8 and 9 while writing sequential-looking 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 wire 8080]

    vwait ::done
    close $server
}

proc wire {conn clientAddr clientPort} {
    # It is necessary to either disable buffering or put it in line mode or
    # to flush the socket every time you send the boundary delimiter.
    chan configure $conn \
        -blocking false \
        -buffering none \
        -translation binary \

    chan event $conn readable [list coroutine coro-$conn serve $conn]
}

proc serve conn {
    chan event $conn readable {}
    set task {}

    try {
        set path [process-request $conn]
        if {$path eq {}} return

        set boundary boundary-[expr rand()]
        send {HTTP/1.1 200 OK}
        send "Content-Type:\
            multipart/x-mixed-replace;boundary=\"$boundary\"\r\n"
        send --$boundary

        for {set i 0} {$i < 3} {incr i} {
            send "Content-Type: text/plain\r\n"
            send {Hello,       text       }
            send --$boundary
            set task [after 500 [info coroutine]]
            yield

            send "Content-Type: text/plain\r\n"
            send {       plain      world!}
            send --$boundary
            set task [after 500 [info coroutine]]
            yield
        }

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

        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>"

            if {$i == $len} {
                send "<img src=\"$::image\">--$boundary--"
            } else {
                send --$boundary
            }

            set task [after 300 [info coroutine]]
            yield
        }
    } trap {POSIX EPIPE} {_ opts} {
        after cancel $task
        return
    } finally {        after cancel $task
        close $conn
    }
}

proc process-request conn {
    if {![regexp {GET (/[^ ]*) HTTP} [read $conn] _ path]} {
        send [status-phrase-response 400 {Bad Request}]
        return
    }

    if {$path ne {/}} {
        if {$path eq {/quit}} {
            send [status-phrase-response 202 Accepted]
            set ::done true
        } else {
            send [status-phrase-response 404 {Not Found}]
        }

        return
    }

    return $path
}
proc call-if-exists command {
    if {![info exists $command]} return
    tailcall $command
}

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

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

proc status-phrase-response {code phrase} {
    append h "HTTP/1.1 $code $phrase\r\n"
    append h "Content-Type: text/plain\r\n\r\n$phrase."
}

main
======


** See also **

   * https://docstore.mik.ua/orelly/web2/xhtml/ch13_03.htm%|%“Server-Push Documents”%|% in ''HTML & XHTML: The Definitive Guide, 4th Edition''.

<<categories>> Example | Web