If you have a php -S localhost:8888 server running, and then type the following commands on your terminal:
exec 3<> /dev/tcp/localhost/8888
echo -e "GET /hello.html HTTP/1.1\nHost: localhost\n" >& 3
cat <& 3
The php server will happily accept the request, and give back a response.
However, if I try the same exercise with jwebserver, jwebserver never receives this manual request. It only seems to respond to requests made from the webbrowser.
Any idea why?
UPDATE: adding more details as requested.
when the above bash scriptis run while a php -S localserver:8888
server is running, the bash script returns:
HTTP/1.1 200 OK
Host: localhost
Date: Mon, 11 Nov 2024 15:24:25 GMT
Connection: close
Content-Type: text/html; charset=UTF-8
Content-Length: 127
<html>
<head>
<title>Simple Hello page!</title>
</head>
<body>
This is a very simple webpage!
</body>
</html>
(where the HTTP body corresponds to the contents of the 'hello.html' file specified).
When the jwebserver is run on the same directory, e.g. as jwebserver -b localhost -p 8888
, the bash script exits (after a noticeable delay) without returning anything, and there's no indication on the jwebserver process that any connection has been attempted in terms of output.
If I monitor each attempt using sudo tcpdump -i any port 8888
then I get the following output for the php one:
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
15:24:25.791267 lo In IP view-localhost.47000 > view-localhost.8888: Flags [S], seq 1728240080, win 65495, options [mss 65495,sackOK,TS val 1603759113 ecr 0,nop,wscale 7], length 0
15:24:25.791295 lo In IP view-localhost.8888 > view-localhost.47000: Flags [S.], seq 239897535, ack 1728240081, win 65483, options [mss 65495,sackOK,TS val 1603759113 ecr 1603759113,nop,wscale 7], length 0
15:24:25.791316 lo In IP view-localhost.47000 > view-localhost.8888: Flags [.], ack 1, win 512, options [nop,nop,TS val 1603759113 ecr 1603759113], length 0
15:24:25.791424 lo In IP view-localhost.47000 > view-localhost.8888: Flags [P.], seq 1:26, ack 1, win 512, options [nop,nop,TS val 1603759114 ecr 1603759113], length 25
15:24:25.791437 lo In IP view-localhost.8888 > view-localhost.47000: Flags [.], ack 26, win 512, options [nop,nop,TS val 1603759114 ecr 1603759114], length 0
15:24:25.791454 lo In IP view-localhost.47000 > view-localhost.8888: Flags [P.], seq 26:42, ack 1, win 512, options [nop,nop,TS val 1603759114 ecr 1603759114], length 16
15:24:25.791460 lo In IP view-localhost.8888 > view-localhost.47000: Flags [.], ack 42, win 512, options [nop,nop,TS val 1603759114 ecr 1603759114], length 0
15:24:25.791473 lo In IP view-localhost.47000 > view-localhost.8888: Flags [P.], seq 42:43, ack 1, win 512, options [nop,nop,TS val 1603759114 ecr 1603759114], length 1
15:24:25.791479 lo In IP view-localhost.8888 > view-localhost.47000: Flags [.], ack 43, win 512, options [nop,nop,TS val 1603759114 ecr 1603759114], length 0
15:24:25.791549 lo In IP view-localhost.8888 > view-localhost.47000: Flags [P.], seq 1:154, ack 43, win 512, options [nop,nop,TS val 1603759114 ecr 1603759114], length 153
15:24:25.791562 lo In IP view-localhost.47000 > view-localhost.8888: Flags [.], ack 154, win 511, options [nop,nop,TS val 1603759114 ecr 1603759114], length 0
15:24:25.791586 lo In IP view-localhost.8888 > view-localhost.47000: Flags [P.], seq 154:281, ack 43, win 512, options [nop,nop,TS val 1603759114 ecr 1603759114], length 127
15:24:25.791593 lo In IP view-localhost.47000 > view-localhost.8888: Flags [.], ack 281, win 511, options [nop,nop,TS val 1603759114 ecr 1603759114], length 0
15:24:25.791626 lo In IP view-localhost.8888 > view-localhost.47000: Flags [F.], seq 281, ack 43, win 512, options [nop,nop,TS val 1603759114 ecr 1603759114], length 0
15:24:25.793618 lo In IP view-localhost.47000 > view-localhost.8888: Flags [F.], seq 43, ack 282, win 512, options [nop,nop,TS val 1603759116 ecr 1603759114], length 0
15:24:25.793653 lo In IP view-localhost.8888 > view-localhost.47000: Flags [.], ack 44, win 512, options [nop,nop,TS val 1603759116 ecr 1603759116], length 0
^C
16 packets captured
32 packets received by filter
0 packets dropped by kernel
whereas I get the following in the jwebserver scenario:
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
15:24:55.964177 lo In IP view-localhost.32904 > view-localhost.8888: Flags [S], seq 4267327301, win 65495, options [mss 65495,sackOK,TS val 1603789286 ecr 0,nop,wscale 7], length 0
15:24:55.964198 lo In IP view-localhost.8888 > view-localhost.32904: Flags [S.], seq 3040620199, ack 4267327302, win 65483, options [mss 65495,sackOK,TS val 1603789286 ecr 1603789286,nop,wscale 7], length 0
15:24:55.964218 lo In IP view-localhost.32904 > view-localhost.8888: Flags [.], ack 1, win 512, options [nop,nop,TS val 1603789286 ecr 1603789286], length 0
15:24:55.964302 lo In IP view-localhost.32904 > view-localhost.8888: Flags [P.], seq 1:26, ack 1, win 512, options [nop,nop,TS val 1603789286 ecr 1603789286], length 25
15:24:55.964310 lo In IP view-localhost.8888 > view-localhost.32904: Flags [.], ack 26, win 512, options [nop,nop,TS val 1603789286 ecr 1603789286], length 0
15:24:55.964322 lo In IP view-localhost.32904 > view-localhost.8888: Flags [P.], seq 26:42, ack 1, win 512, options [nop,nop,TS val 1603789286 ecr 1603789286], length 16
15:24:55.964328 lo In IP view-localhost.8888 > view-localhost.32904: Flags [.], ack 42, win 512, options [nop,nop,TS val 1603789286 ecr 1603789286], length 0
15:24:55.964336 lo In IP view-localhost.32904 > view-localhost.8888: Flags [P.], seq 42:43, ack 1, win 512, options [nop,nop,TS val 1603789286 ecr 1603789286], length 1
15:24:55.964340 lo In IP view-localhost.8888 > view-localhost.32904: Flags [.], ack 43, win 512, options [nop,nop,TS val 1603789286 ecr 1603789286], length 0
^C
9 packets captured
19 packets received by filter
Whereas if I try to interact with the jwebserver on a normal browser, by going to location localhost:8888/hello.html, I get a normal response from the jwebserver, with terminal output indicating the request was received and responded to, and a tcpdump output of:
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
15:32:47.505277 lo In IP view-localhost.45798 > view-localhost.8888: Flags [P.], seq 687846126:687846634, ack 397941893, win 512, options [nop,nop,TS val 1604260827 ecr 1604254275], length 508
15:32:47.508648 lo In IP view-localhost.8888 > view-localhost.45798: Flags [P.], seq 1:149, ack 508, win 512, options [nop,nop,TS val 1604260831 ecr 1604260827], length 148
15:32:47.508661 lo In IP view-localhost.45798 > view-localhost.8888: Flags [.], ack 149, win 511, options [nop,nop,TS val 1604260831 ecr 1604260831], length 0
15:32:47.520045 lo In IP view-localhost.8888 > view-localhost.45798: Flags [P.], seq 149:276, ack 508, win 512, options [nop,nop,TS val 1604260842 ecr 1604260831], length 127
15:32:47.520065 lo In IP view-localhost.45798 > view-localhost.8888: Flags [.], ack 276, win 511, options [nop,nop,TS val 1604260842 ecr 1604260842], length 0
^C
5 packets captured
10 packets received by filter
\r\n
Your HTTP request is malformed.
This Comment by dave_thompson_085 explains the issue: Use \r\n
rather than \n
.
Change this line of your bash script:
echo -e "GET /hello.html HTTP/1.1\nHost: localhost\n" >& 3
… to this:
echo -e "GET /hello.html HTTP/1.1\r\nHost: localhost\r\n" >& 3
The HTTP/1.1 specification in RFC 2616 requires the use of CARRIAGE RETURN + LINE FEED (CRLF) as line terminator. To quote:
HTTP/1.1 defines the sequence CR LF as the end-of-line marker for all protocol elements except the entity-body
More specifically, the spec explicitly says each message-header must be followed by a CRLF. To quote:
generic-message = start-line *(message-header CRLF) CRLF [ message-body ]
Apparently your PHP implementation fails to comply with the specification, while the jwebserver (JEP 408) implementation complies correctly.
Using Java 23.0.1 on macOS Sonoma 14.7.1, in Terminal.app, I launch jwebserver with:
jwebserver -p 8888
In another Terminal.app window I switch my shell to bash. (zsh did not like your script.)
I run your exact text including its incorrect headers with \n
.
exec 3<> /dev/tcp/localhost/8888
echo -e "GET /hello.html HTTP/1.1\nHost: localhost\n" >& 3
cat <& 3
After a long moment, the command-line simply returns.
bash-3.2$ exec 3<> /dev/tcp/localhost/8888
bash-3.2$ echo -e "GET /hello.html HTTP/1.1\nHost: localhost\n" >& 3
bash-3.2$ cat <& 3
bash-3.2$
No content, no error, on the client side. Meanwhile, the server window does nothing, no error, no message. Apparently jwebserver simply discards the invalid request.
I open another Terminal.app window for another client request, switching to bash.
This time I include the required \r\n
rather than mere \n
. This works! I get the content of my little web page displayed on the console.
bash-3.2$ exec 3<> /dev/tcp/localhost/8888
bash-3.2$ echo -e "GET /hello.html HTTP/1.1\r\nHost: localhost\r\n" >& 3
bash-3.2$ cat <& 3
HTTP/1.1 200 OK
Date: Tue, 12 Nov 2024 04:33:17 GMT
Last-modified: Tue, 12 Nov 2024 03:54:39 GMT
Content-type: text/html
Content-length: 290
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>title</title>
</head>
<body>
<h1>Bonjour</h1>
<p>Wazzup?</p>
</body>
</html>
bash-3.2$
Meanwhile, in the Terminal.app window for the running jwebserver, I see a line appear reporting the activity:
127.0.0.1 - - [12/Nov/2024:04:32:37 +0000] "GET /hello.html HTTP/1.1" 200 -
So the problem is your invalid request that fails to comply with the HTTP 1.1 spec. Fix your request to comply, and jwebserver will serve you.
And file a bug report with your PHP server implementation for failing to follow the HTTP specification.