Sending Raw HTTP in PowerShell — Experiencing HTTP over TCP on localhost

ネットワーク・RFCカテゴリを表すパンダのイラスト Networking / RFC

About this article
This article was created using an automated generation workflow leveraging generative AI. It verifies the HTTP/1.1 message structure of RFC 9112 and the .NET TcpClient / NetworkStream APIs, implementing them as a localhost experiment without connecting to external sites.

Verification Status: 📘 RFC & official .NET APIs verified, localhost sample implemented, PowerShell live verification not yet performed

Sending Raw HTTP with PowerShell: Experiencing HTTP over TCP on Localhost

HTTP is not TCP itself. In HTTP/1.1, messages consisting of a request line, header fields, an empty line, and optionally a body are exchanged over a TCP connection.

Normally, Invoke-WebRequest or browsers hide these details, but TcpClient writing strings directly to the stream makes them easier to see.

Keeping it entirely on a single PC this time

Instead of using an external website as a testbed, we will open two PowerShell windows.

  1. In one, 127.0.0.1:8085 start a simple HTTP server

  2. In the other, establish a TCP connection

  3. Send an HTTP/1.1 GET request

  4. The server displays the received content

  5. 200 OK returns

sequenceDiagram
    participant C as PowerShell Client
    participant T as TCP localhost:8085
    participant S as PowerShell Server
    C->>T: TCP connect
    T->>S: connection accepted
    C->>S: GET / HTTP/1.1 + headers + CRLF CRLF
    S-->>C: HTTP/1.1 200 OK + headers + body
    S-->>C: TCP close

The HTTP being sent looks like a plain string

The core of the client side is as follows.

$request = @(
    "GET / HTTP/1.1"
    "Host: localhost:8085"
    "Connection: close"
    ""
    ""
) -join "`r`n"

$requestBytes = [System.Text.Encoding]::ASCII.GetBytes($request)
$stream.Write($requestBytes, 0, $requestBytes.Length)
$stream.Flush()

The HTTP/1.1 request line is GET / HTTP/1.1. This is followed by header fields, and the header section is terminated with an empty line.

Here, "" are placed side-by-side and joined with CRLF to create the final CRLF CRLF at the end.

Do not omit the Host header

RFC 9112 requires the Host header field in HTTP/1.1 requests. Just because this is educational material, sending only GET / HTTP/1.1 would hide an essential part of HTTP/1.1.

Therefore, even in this localhost experiment, we explicitly specify the following.

Host: localhost:8085

TcpClient does not know HTTP

.NET 's TcpClient is a class that handles TCP connections.GetStream() obtained via NetworkStream sends and receives byte arrays.

In other words, in this code,

flowchart TB
    A[HTTPの文字列] --> B[ASCII bytes]
    B --> C[NetworkStream]
    C --> D[TCP]
    D --> E[localhost server]

we are manually building the layer of

Once you grasp this concept, it becomes easier to understand that HTTP client APIs 'construct messages according to HTTP rules and use a transport layer underneath.'

The response Content-Length is the number of bytes, not characters

On the server side, the body is converted to UTF-8 before constructing the Content-Length.

$body = "Hello from localhost HTTP over TCP`n"
$bodyBytes = [System.Text.Encoding]::UTF8.GetBytes($body)

$header = @(
    "HTTP/1.1 200 OK"
    "Content-Type: text/plain; charset=utf-8"
    "Content-Length: $($bodyBytes.Length)"
    "Connection: close"
    ""
    ""
) -join "`r`n"

Content-Length represents the number of octets, so it is important not to directly use the character count of .Length when including multi-byte characters like Japanese in the body.

HTTPS is not exactly like this

This tutorial uses plain-text HTTP. In HTTPS, TLS is layered on top of TCP, and HTTP is exchanged over that.

HTTP
 TLS
  TCP
   IP

Therefore, simply sending an HTTP string to port 443 using this sample will not result in valid HTTPS communication. It can serve as an entry point when you learn TLS next.

GitHub Sample

Official & Primary Sources

Document information

Article title
Sending Raw HTTP in PowerShell — Experiencing HTTP over TCP on localhost
Published
Updated
Source
https://papanda925.com/?p=15271&lang=en

License: Text and original figures for which this site holds the relevant rights are available under CC BY 4.0 , unless otherwise noted. This article may include content created or edited with generative AI. If code has a separate license notice or a linked GitHub repository license, that license takes precedence for the code. Quotations, third-party materials, images, and trademarks are excluded from this license. Usage policy

Copied title and URL