/multihttp

How to start both HTTP ans HTTPS server on same port

Primary LanguageGo

multihttp

How to start both HTTP ans HTTPS servers on same port ?

Description

The solution to this exercice is to read and analyse the first byte of the communication only.

Simple HTTP protocol starts directly with the HTTP verb:

  • GET
  • POST
  • HEAD
  • DELETE
  • ...

So that the first byte is either G, P, H, D, ...

HTTPS communication starts with the SSL handshake. And in this case the first byte always is the character with the code 22.

In this simple multihttp library once we got the first byte we send it to the the right server (HTTP or HTTPS depending on its value) with the rest of the communication.

The only thing you have to do is to decribe a serveMux with its route handlers list. Then run the multihttp server with this serveMux. As for simple net/http library the server starts with either function

func MultiListenAndServe(addr string, handler http.Handler, certFile, keyFile string) error

or

func MultiServe(ln net.Listener, handler http.Handler, certFile, keyFile string) error

Of course, to be able to serve SSL communications a private key (key.pem) and a valid certificate (cert.pem) must be provided.

Examples

Hello world

Here is a simple Hello World!:

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/cyd01/multihttp"
)

var (
    addr     = "127.0.0.1:8080"
    certFile = "cert.pem"
    keyFile  = "key.pem"
)

func hello(w http.ResponseWriter, r *http.Request) {
    log.Println(r.Method, r.URL.Path)
    fmt.Fprintln(w, "Hello world!")
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", hello)

    log.Println("Starting hello server at", addr)
    log.Fatal(multihttp.MultiListenAndServe(addr, mux, certFile, keyFile))
}

Build it with command go build ./cmd/hello and start it with ./hello.

The server is reachable at http://localhost:8080 and https://localhost:8080.

File server

A simple file server example is available.

Build it with command go build ./cmd/fileserver.

Proxy

Another simple example to proxify towards another HTTP server is available.

Build it with command go build ./cmd/proxy.

Complement

How to easily build a server certificate

Make a light certificate authority

openssl genrsa -out cakey.pem 4096 \
&& openssl req -x509 -sha512 -days 3650 \
  -new -noenc \
  -key cakey.pem \
  -outform PEM -out cacert.pem \
  -addext "basicConstraints = critical, CA:TRUE" \
  -addext "subjectKeyIdentifier = hash" \
  -addext "authorityKeyIdentifier = keyid:always, issuer" \
  -addext "keyUsage = critical, keyCertSign, cRLSign, digitalSignature" \
  -subj "/C=FR/ST=France/L=Paris/O=Orga/OU=Unit/CN=EasyCA" \
&& sudo cp cacert.pem /usr/local/share/ca-certificates/easyca.crt \
&& sudo update-ca-certificates

Make server certificate

openssl req -new \
  -newkey rsa:4096 -nodes -keyform PEM -keyout key.pem \
  -outform PEM -out csr.pem \
  -subj "/C=FR/ST=France/L=Paris/O=Orga/OU=Unit/CN=localhost" \
  -addext "subjectAltName = IP:127.0.0.1,DNS:localhost" \
&& openssl x509 -req -days 3650 -sha512 \
  -CA cacert.pem -CAkey cakey.pem -CAcreateserial \
  -in csr.pem \
  -copy_extensions copy \
  -outform PEM -out cert.pem

Remove certificate authority

sudo rm -f /usr/local/share/ca-certificates/easyca.crt \
&& sudo update-ca-certificates -f

Ref:
https://groups.google.com/g/golang-nuts/c/4oZp1csAm2o/m/nTTKDvvFJQ0J
https://stackoverflow.com/questions/26090301/run-both-http-and-https-in-same-program