Write a program that accepts HTTP requests on any port of your choice above 1024, and responds to requests on any URL of your choice with its own source code. It must work with, at least, Firefox and Chrome. The code should be delivered (削除) as (removed requirement because none of the answers but one met it) with no formatting. There is no restriction on the URL, except that it must not be used to encode any portion of the code; serving on the root is encouraged and likely the shortest solution, but is not required.text/plain
(削除ここまで)
Standard quine restrictions apply. Web server frameworks are allowed, but are considered separate languages, so, for example, a Python + Flask answer does not compete with a stdlib-only Python answer.
Bonus for HTTPS support:
HTTPS | HTTP | Bonus |
---|---|---|
No | Yes | None |
Yes | No | 10% |
Yes | Yes | 15% |
Yes | Redirects to HTTPS | 20% |
If you do include HTTPS support, you may access the certificate by any reasonable means.
-
2\$\begingroup\$ Does it need to respond to multiple requests? \$\endgroup\$gsitcia– gsitcia2023年11月30日 03:12:40 +00:00Commented Nov 30, 2023 at 3:12
-
\$\begingroup\$ @gsitcia no, except that to claim the 20% bonus for redirecting HTTP to HTTPS, it must be able to handle the initial HTTP request and the resulting HTTPS request. Otherwise, it's okay if it closes after one request. \$\endgroup\$Someone– Someone2023年11月30日 03:59:46 +00:00Commented Nov 30, 2023 at 3:59
-
1\$\begingroup\$ Is it okay if the port is chosen randomly at startup? \$\endgroup\$corvus_192– corvus_1922023年11月30日 19:44:46 +00:00Commented Nov 30, 2023 at 19:44
-
\$\begingroup\$ @corvus_192 that is okay, as long as there is some indication of what port is used, such as printing it to stdout \$\endgroup\$Someone– Someone2023年11月30日 19:47:50 +00:00Commented Nov 30, 2023 at 19:47
-
1\$\begingroup\$ Is it cheating to just serve the source file? \$\endgroup\$noodle person– noodle person2023年12月05日 14:00:54 +00:00Commented Dec 5, 2023 at 14:00
11 Answers 11
Javascript (Node.js), 77 bytes
(f=x=>require('http').createServer((_,x)=>x.end(`(f=${f})()`)).listen(1e4))()
-
\$\begingroup\$ 76 bytes with express:
(f=x=>require('express')().get('',(_,r)=>r.end(
(f=${f})())).listen(1e4))()
\$\endgroup\$corvus_192– corvus_1922023年11月30日 19:47:53 +00:00Commented Nov 30, 2023 at 19:47 -
\$\begingroup\$ @corvus_192 Not vanilla and
Web server frameworks are allowed, but are considered separate languages
\$\endgroup\$l4m2– l4m22023年12月01日 00:31:00 +00:00Commented Dec 1, 2023 at 0:31 -
\$\begingroup\$ I know, but the code is so similar, I didn't want to post it as a separate answer. \$\endgroup\$corvus_192– corvus_1922023年12月01日 11:28:31 +00:00Commented Dec 1, 2023 at 11:28
Python 3, 176 bytes
_='_=%r;a=%r;exec(a)';a="from socket import*;s=socket();s.bind(('',9000));s.listen()\nwhile 1:q,c=s.accept();q.send(b'HTTP/1.0\\n\\n'+(_%(_,a)).encode());q=q.recv(999)";exec(a)
Listens to any requests to localhost:9000
.
Only listening for one request saves 11 bytes
165 bytes
_='_=%r;a=%r;exec(a)';a="from socket import*;s=socket();s.bind(('',9000));s.listen();q,c=s.accept();q.send(b'HTTP/1.0\\n\\n'+(_%(_,a)).encode());q.recv(999)";exec(a)
Both browsers (Firefox 120.0 & Chromium 119.0.6045.159) seem to be happy to accept responses with no status code.
147 bytes (only Firefox)
_='_=%r;a=%r;exec(a)';a="from socket import*;s=socket();s.bind(('',9000));s.listen();q,c=s.accept();q.send((_%(_,a)).encode());q.recv(999)";exec(a)
Firefox still supports HTTP/0.9
146 bytes (only Chromium)
_='_=%r;a=%r;exec(a)';a="from socket import*;s=socket();s.bind(('',9000));s.listen();s.accept()[0].send(b'HTTP\\n\\n'+(_%(_,a)).encode())";exec(a)
Chromium doesn't seem to require the '/1.0' part, and also doesn't care if the server closes the connection early (?)
-
1\$\begingroup\$ Can you replace
9000
with9e3
or even-1
for port65535
? \$\endgroup\$ATaco– ATaco2023年11月30日 03:57:56 +00:00Commented Nov 30, 2023 at 3:57 -
\$\begingroup\$ @ATaco I just tried it, 9e3 complains about being a float, and -1 gives an OverflowError \$\endgroup\$gsitcia– gsitcia2023年11月30日 04:36:30 +00:00Commented Nov 30, 2023 at 4:36
-
\$\begingroup\$ Huh, I wonder why Firefox requires Content-Length? \$\endgroup\$Neil– Neil2023年11月30日 08:31:04 +00:00Commented Nov 30, 2023 at 8:31
-
1\$\begingroup\$ Actually testing it locally it's intermittent... \$\endgroup\$Neil– Neil2023年11月30日 10:51:57 +00:00Commented Nov 30, 2023 at 10:51
-
1\$\begingroup\$ You can remove the space after
import
infrom socket import *
. \$\endgroup\$Unmitigated– Unmitigated2023年11月30日 22:07:54 +00:00Commented Nov 30, 2023 at 22:07
Python 3 + http.server, (削除) 134 (削除ここまで) 95 bytes
exec(b:="import os;open('.txt','w').write('exec(b:=%r)'%b);os.system('python -m http.server')")
Uses a simple Exec quine and writes the contents of it to .txt
and then launches a simple webserver in the current folder via the python -m http.server
tool. Hosts on port 8000
at /.txt
Very rarely use Python, so it might be golfable...
-
2\$\begingroup\$ I haven't tested it, but knowing Python, can't you remove the space at
) as
? \$\endgroup\$Kevin Cruijssen– Kevin Cruijssen2023年11月30日 07:45:55 +00:00Commented Nov 30, 2023 at 7:45 -
1\$\begingroup\$ You can provide a single string for the
call
argument:call(['python','-m','http.server'])
->call('python -m http.server')
, removing 6 bytes. \$\endgroup\$SevC_10– SevC_102023年11月30日 17:27:37 +00:00Commented Nov 30, 2023 at 17:27 -
\$\begingroup\$ I removed the text/plain requirement because the other answers weren't meeting it, so I think you can save three bytes now with a one-character file name. \$\endgroup\$Someone– Someone2023年12月01日 19:50:47 +00:00Commented Dec 1, 2023 at 19:50
-
1\$\begingroup\$ @Somebody Unfortunately has to be
.txt
as otherwise it tries to download the file, rather than render it. \$\endgroup\$ATaco– ATaco2023年12月02日 04:59:39 +00:00Commented Dec 2, 2023 at 4:59 -
1\$\begingroup\$ @ATaco I think the challenge was to return an HTTP response with the source code, so IMO downloading is valid \$\endgroup\$Seggan– Seggan2023年12月03日 03:09:01 +00:00Commented Dec 3, 2023 at 3:09
Python + Flask, 151 bytes
from flask import*;a=Flask('');s="from flask import*;a=Flask('');s=%s\[email protected]('/')\ndef i():return s%%repr(s)"
@a.route('/')
def i():return s%repr(s)
Doesn't show newlines as it's text/html
. Visible on localhost:5000
and/or 127.0.0.1:5000
. The obvious python and flask solution.
To be executed as a flask app. For example (on windows at least), assuming the file is called flask_app.py
:
> set FLASK_APP=flask_app
> flask run
-
\$\begingroup\$ Can't be bothered to test it, but does this work by any chance? \$\endgroup\$Unrelated String– Unrelated String2023年11月30日 04:55:53 +00:00Commented Nov 30, 2023 at 4:55
-
1\$\begingroup\$ Why don't you use exec to run thethe code in the string and avoid having to duplicate it? \$\endgroup\$The_spider– The_spider2023年11月30日 20:26:51 +00:00Commented Nov 30, 2023 at 20:26
-
\$\begingroup\$ "Doesn't show newlines as it's text/plain." What? did you mean text/html? \$\endgroup\$Oskar Skog– Oskar Skog2023年12月01日 09:26:32 +00:00Commented Dec 1, 2023 at 9:26
-
\$\begingroup\$ @OskarSkog yes, I indeed meant text/html \$\endgroup\$2023年12月01日 09:48:44 +00:00Commented Dec 1, 2023 at 9:48
JavaScript (Deno), (削除) 60 (削除ここまで) 52 bytes
function f(){Deno.serve(_=>new Response(f+"f()")}f()
Try it at the Deno Deploy Playground! (Outdated).
Saved 5 bytes by using a function
declaration instead of an arrow function, because Deno runs in strict mode where you need let
in variable declarations:
let f=_=>...`let f=${f};f()`...;f()
function f(){...f+"f()"...}f()
// allowed in Node, but not in Deno:
f=_=>...`f=${f};f()`...;f()
Here's an alternate version that doesn't use function stringifying, (削除) stolen (削除ここまで) slightly modified from Don Hastings's wonderful quine, for 88 bytes:
let Q="let q=Q.link()[8];Deno.serve(()=>new Response(`let Q=${q+Q+q};eval(Q)`))";eval(Q)
Lua + LuaSockets, 138 bytes.
b="t=require('socket').tcp()t:bind('*',-1)t:listen()c=t:accept()c:receive()c:send(('HTTP/1.0 200\\n\\nb=%qload(b)()'):format(b))"load(b)()
Stores the main code in b
and loads it, then wraps it in ('...b=%qload(b)()'):format(b)
to quine it.
The webserver listens for any TCP connection on port 65535
(-1) and just immediately dumps the bare-minimum HTTP response to it.
Racket #lang web-server/insta
, 199 bytes
#lang web-server/insta (define-syntax-rule(S n f)(define n(f'(S n f))))(S start (λ (s) (λ r (response/xexpr (~a "#lang web-server/insta (define-syntax-rule(S n f)(define n(f'(S n f))))" (~s s))))))
Running the program (e.g. with racket golf.rkt
or in DrRacket) prints something like:
Your Web application is running at http://localhost:34163/servlets/standalone.rkt.
Stop this program at any time to terminate the Web Server.
and automatically launches your default browser with the page. Stopping the program (e.g. with Ctrl-C or DrRacket's Stop button) prints:
Web Server stopped.
Depending on your definition of "standard library", web-server/insta
might be part of Racket's standard library. It is included in the "main distribution" you'd download from https://download.racket-lang.org.
HTTPS Only, 241 bytes
#lang web-server (define-syntax-rule(S s)(s'(S s)))(S (λ (s) (local-require web-server/servlet-env) (serve/servlet (λ r (response/xexpr (~a "#lang web-server (define-syntax-rule(S s)(s'(S s)))" (~s s)))) #:ssl? #t #:server-root-path ".")))
This uses server-cert.pem
and private-key.pem
from the current directory. It serves on https://localhost:8000/servlets/standalone.rkt
.
HTTP→HTTPS, 356 bytes
#lang web-server (define-syntax-rule(S s)(s'(S s)))(S (λ (s) (local-require web-server/servlet-env) (thread (λ () (serve/servlet (λ r (redirect-to "https://localhost:8443/servlets/standalone.rkt"))))) (serve/servlet (λ r (response/xexpr (~a "#lang web-server (define-syntax-rule(S s)(s'(S s)))" (~s s)))) #:port 8443 #:ssl? #t #:server-root-path ".")))
Same certificate configuration as above, but listens on http://localhost:8000/servlets/standalone.rkt
and https://localhost:8443/servlets/standalone.rkt
.
Original: With text/plain
, 263 bytes
#lang web-server/insta (define-syntax-rule (S n f) (define n (f '(S n f)))) (S start (λ (s) (λ r (response/output #:mime-type #"text/plain;charset=utf-8" (λ (o) (fprintf o "#lang web-server/insta (define-syntax-rule (S n f) (define n (f '(S n f)))) ~s" s))))))
-
\$\begingroup\$ It's a shame we can't save a few more bytes by removing spaces in the body of
start
; I tried, and (I think because of the macro) the response puts them back in. \$\endgroup\$D. Ben Knoble– D. Ben Knoble2023年12月05日 14:47:15 +00:00Commented Dec 5, 2023 at 14:47 -
\$\begingroup\$ @D.BenKnoble I tried, too: the issue is that
write
emits conventional whitespace, not the minimum possible whitespace. \$\endgroup\$LiberalArtist– LiberalArtist2023年12月05日 17:10:00 +00:00Commented Dec 5, 2023 at 17:10
PowerShell Core, 149 bytes
$f={($h=[Net.HttpListener]::new()).Prefixes.Add("http://*:9090/")
$h|% s*t
(($r=$h|% G*t|% R*e)|% o*).Write([byte[]]("`$f={$f}"|% t*y))
$r.Close()}
Run it using &$f
Listens on port 9090
Replies only once and does not free the port
Rust, (削除) 385 (削除ここまで) 373 bytes
use{std::*,io::*};fn main(){write!(net::TcpListener::bind("[::1]:8080").unwrap().accept().unwrap().0,r"HTTP/1.1 200
Content-Type:text/plain
Content-Length:373
{1}{1}{}","\x22#);}",r#"use{std::*,io::*};fn main(){write!(net::TcpListener::bind("[::1]:8080").unwrap().accept().unwrap().0,r"HTTP/1.1 200
Content-Type:text/plain
Content-Length:373
{1}{1}{}","\x22#);}",r#""#);}
When run as the main.rs file in a cargo project with cargo run
, this program waits until you go to [::1]:8080 (ipv6 loopback) in your browser (this is what accept()
does) and then sends over its source code and ends.
C (gcc -lws2_32
+ winsock2.h
), 408 bytes
Z;main(){char*S,Q[16]={2,0,4,1},*M=malloc(999);WSAStartup(9,M);sprintf(M,S="HTTP/1.1 200 OK%c%cContent-Length: 408%c%c%c%cZ;main(){char*S,Q[16]={2,0,4,1},*M=malloc(999);WSAStartup(9,M);sprintf(M,S=%c%s%c,13,10,13,10,13,10,34,S,34);bind(Z=socket(2,1,0),Q,16);for(listen(Z,3);;)send(accept(Z,Q,0),M,448,0);}",13,10,13,10,13,10,34,S,34);bind(Z=socket(2,1,0),Q,16);for(listen(Z,3);;)send(accept(Z,Q,0),M,448,0);}
This uses the Windows Socket 2 API. I have made many questionable choices for the sake of the golf, so there is likely some instability. Listens to an arbitrary number of HTTP requests on port 1025
. Compiled with gcc server.c -o server -lws2_32
(e.g.).
Verification
In Firefox:
A picture of the Quine in action on Firefox, showing the full code from earlier.
[![A picture of the Quine in action on Firefox, showing the full code from earlier.][2]][2]
Here is the Ruby test harness I used to verify integrity:
require "net/http"
puts "Waiting for server to start..."
server_process = IO.popen("server.exe") { |server|
3.times { |i|
response = Net::HTTP.get_response(URI("http://localhost:1025")).body
puts "Response ##{i} obtained:"
puts response
puts "Same as server.c?"
puts File.read("server.c") == response
}
puts "Killing server..."
Process.kill("KILL", server.pid)
}
Which, when run:
λ ruby test-server.rb
Waiting for server to start...
Response #0 obtained:
Z;main(){char*S,Q[16]={2,0,4,1},*M=malloc(999);WSAStartup(9,M);sprintf(M,S="HTTP/1.1 200 OK%c%cContent-Length: 408%c%c%c%cZ;main(){char*S,Q[16]={2,0,4,1},*M=malloc(999);WSAStartup(9,M);sprintf(M,S=%c%s%c,13,10,13,10,13,10,34,S,34);bind(Z=socket(2,1,0),Q,16);for(listen(Z,3);;)send(accept(Z,Q,0),M,448,0);}",13,10,13,10,13,10,34,S,34);bind(Z=socket(2,1,0),Q,16);for(listen(Z,3);;)send(accept(Z,Q,0),M,448,0);}
Same as server.c?
true
Response #1 obtained:
Z;main(){char*S,Q[16]={2,0,4,1},*M=malloc(999);WSAStartup(9,M);sprintf(M,S="HTTP/1.1 200 OK%c%cContent-Length: 408%c%c%c%cZ;main(){char*S,Q[16]={2,0,4,1},*M=malloc(999);WSAStartup(9,M);sprintf(M,S=%c%s%c,13,10,13,10,13,10,34,S,34);bind(Z=socket(2,1,0),Q,16);for(listen(Z,3);;)send(accept(Z,Q,0),M,448,0);}",13,10,13,10,13,10,34,S,34);bind(Z=socket(2,1,0),Q,16);for(listen(Z,3);;)send(accept(Z,Q,0),M,448,0);}
Same as server.c?
true
Response #2 obtained:
Z;main(){char*S,Q[16]={2,0,4,1},*M=malloc(999);WSAStartup(9,M);sprintf(M,S="HTTP/1.1 200 OK%c%cContent-Length: 408%c%c%c%cZ;main(){char*S,Q[16]={2,0,4,1},*M=malloc(999);WSAStartup(9,M);sprintf(M,S=%c%s%c,13,10,13,10,13,10,34,S,34);bind(Z=socket(2,1,0),Q,16);for(listen(Z,3);;)send(accept(Z,Q,0),M,448,0);}",13,10,13,10,13,10,34,S,34);bind(Z=socket(2,1,0),Q,16);for(listen(Z,3);;)send(accept(Z,Q,0),M,448,0);}
Same as server.c?
true
Killing server...
Explanation
Using fairly standard quining techniques, although my gcc on Windows does not support the POSIX extension for referencing existing *printf
arguments (as in this quine, so we must duplicate quite a few constants. At one point, I tried using unprintable characters in multiple places (`Q="\x02\x00''"), but that seemed to not work well with browsers interfacing with the server.
Here is an expanded version, producing the same output as above (so no longer a quine itself), but commented:
// our socket description integer
Z;
// the server code contained here
main() {
char
// our format string to be passed into sprintf
*S,
// shorthand for our struct sockaddr_in, which is passed to bind and accept
// struct sockaddr_in address = { .sin_family = AF_INET /*=2*/, .sin_port = htons(1025) /*=0x0401*/ };
// [NOTE 1]
Q[16] = { 2, 0, 4, 1 },
// M serves two purposes: 1. to be a sprintf buffer and 2. to be allocated memory to allow WSAStartup to run without crashing
*M = malloc(999);
// usually, wVersionRequired is version of windows sockets required (e.g. 2.1 would be 0x0201 = 513), and would be expressed like MAKEWORD(2,3)
// while wVersionRequired can't be 0, from testing, 9 seems to work.
WSAStartup(9, M);
// the main quine section
sprintf(
// the message buffer
M,
// the format string
S = "HTTP/1.1 200 OK%c%cContent-Length: 412%c%c%c%cZ;main(){char*S,Q[16]={2,0,4,1},*M=malloc(999);WSAStartup(514,M);sprintf(M,S=%c%s%c,13,10,13,10,13,10,34,S,34);bind(Z=socket(2,1,0),Q,16);for(listen(Z,3);;)send(accept(Z,Q,0),M,452,0);}",
// \r\n after 200 OK
13, 10,
// \r\n\r\n after headers
13, 10, 13, 10
// " followed by our string followed by ",
34, S, 34,
);
// binds our socket to our target address
bind(
// creates a socket with a particular configuration.
// socket(
// address family specification; AF_INET == 2, and represents IPv4
// the socket type specification; SOCK_STREAM == 1, and represents two-way connections
// the protocol subtype; 0, since we don't need to specify anything further
// )
// returns a socket description integer
Z = socket(2,1,0),
// the string from earlier, reinterpreted as an address struct
Q,
// the size of the address struct; expected to be 16
16
);
// infinite loop
for(
// but first, to save a semicolon, start listening on our socket
// we arbitrarily choose 3 as a backlog queue size
listen(Z, 3);
;
) {
// reply to the request with the message
send(
// accept a request on the address for our socket
accept(Z, Q, 0),
// the message to report
M,
// its length
452,
// flags; none chosen
0
);
}
}
Note 1: We can omit the more precise property specification .sin_addr = { .s_addr = INADDR_ANY }
as INADDR_ANY == 0
. If we did need it, we do have the golfy option Q={2,1025,{0}}
.
-
\$\begingroup\$ I just noticed it doesn't have to respond to multiple requests. I'll change the code in a bit. \$\endgroup\$Conor O'Brien– Conor O'Brien2024年03月25日 00:18:40 +00:00Commented Mar 25, 2024 at 0:18
Bash + ncat (from nmap), 106 bytes
h='HTTP/2\r\n\r\n';s='h=47円%s47円;s=47円%s47円;printf $h$s $h $s|ncat -lp2222';printf $h$s $h $s|ncat -lp2222
-
2\$\begingroup\$ it does not return the souce code \$\endgroup\$matteo_c– matteo_c2023年12月02日 17:44:09 +00:00Commented Dec 2, 2023 at 17:44
-
\$\begingroup\$ It does for me in Firefox and Chrome. \$\endgroup\$corvus_192– corvus_1922023年12月03日 11:09:37 +00:00Commented Dec 3, 2023 at 11:09