Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ indent_style = tab
[*.md]
trim_trailing_whitespace = false
eclint_indent_style = unset

[*.yml,*.yaml]
indent_size = 2
46 changes: 23 additions & 23 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
name: Docker Image CI

on:
push:
tags: ["v*.*.*"]
push:
tags: ["v*.*.*"]

env:
DOCKER_USER: marcelstolin
IMAGE_NAME: wildcardhttp
DOCKER_USER: marcelstolin
IMAGE_NAME: wildcardhttp

jobs:
build:
runs-on: ubuntu-latest
build:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log into Docker Hub
uses: docker/login-action@v3
with:
username: ${{ env.DOCKER_USER }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Log into Docker Hub
uses: docker/login-action@v3
with:
username: ${{ env.DOCKER_USER }}
password: ${{ secrets.DOCKER_TOKEN }}

- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@v6
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: "${{ env.DOCKER_USER }}/${{ env.IMAGE_NAME }}:latest,${{ env.DOCKER_USER }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}"
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@v6
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: "${{ env.DOCKER_USER }}/${{ env.IMAGE_NAME }}:latest,${{ env.DOCKER_USER }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}"
64 changes: 39 additions & 25 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -1,29 +1,43 @@
name: Go

name: Go CI
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
push:
branches:
- main
pull_request:
branches:
- main
paths-ignore:
- "**/Readme.md"
- ".editorconfig"
- Readme.md
- .editorconfig

permissions:
contents: read
pull-requests: read
checks: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: "1.23.4"

- name: Build
run: make build

- uses: actions/upload-artifact@v4
with:
name: whttp
path: whttp
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: 1.23.4
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: v1.60
build:
needs: golangci
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: 1.23.4
- name: Build
run: make build
- uses: actions/upload-artifact@v4
with:
name: whttp
path: whttp
8 changes: 8 additions & 0 deletions .prettierrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
semi: false
tabWidth: 4
overrides:
- files:
- "**/*.yml"
- "**/*.yaml"
options:
tabWidth: 2
88 changes: 77 additions & 11 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,41 +1,107 @@
package main

import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/http/httputil"
"os"
"strconv"
"strings"
)

const HelpText = `Usage: whttp <host> [<port>]
const HelpText = `Usage: whttp <host> [<JSON>]

Provide the port number as part of the host or as a single argument.
For example: localhost:5000, :5000, or localhost 5000.`
host : The TCP network host to accept connections on; e.g. localhost:5000 or :5000
JSON : Serve any response in JSON format; Default: false

For example: wget :5000 true`

const PrettyHeaderFormat = "Header\n======\n%s"
const PrettyBodyFormat = "Body\n====\n%s"

type RequestData struct {
Header map[string][]string
Body string
}

func (data RequestData) prettyPrintHeader() string {
var output string
for key, values := range data.Header {
output += fmt.Sprintf("%s: %s\n", key, strings.Join(values, ", "))
}
return output
}

func (data RequestData) PrettyPrint() string {
prettyHeader := fmt.Sprintf(PrettyHeaderFormat, data.prettyPrintHeader())
var prettyBody string
if len(data.Body) > 0 {
prettyBody = fmt.Sprintf(PrettyBodyFormat, data.Body)
}
return fmt.Sprintf("%s\n%s", prettyHeader, prettyBody)
}

func NewRequestData(r *http.Request) (RequestData, error) {
var data RequestData
data.Header = r.Header
if body, err := io.ReadAll(r.Body); err == nil {
data.Body = string(body)
} else {
return data, err
}
return data, nil
}

// This function simply prints a dump of the http request.
func handleAny(w http.ResponseWriter, r *http.Request) {
dump, err := httputil.DumpRequest(r, true)
if err != nil {
if requestData, err := NewRequestData(r); err == nil {
fmt.Fprintf(w, "%s", requestData.PrettyPrint())
} else {
w.WriteHeader(500)
fmt.Fprintf(w, "%e", err)
return
}
}

func handleAnyJson(w http.ResponseWriter, r *http.Request) {
if requestData, err := NewRequestData(r); err == nil {
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(requestData); err != nil {
w.WriteHeader(500)
fmt.Fprintf(w, "%e", err)
return
}
} else {
w.WriteHeader(500)
fmt.Fprintf(w, "%e", err)
return
}
fmt.Fprintf(w, "%s", dump)
}

func main() {
var addr string
var serveJson bool

if len(os.Args[1:]) == 1 {
addr = os.Args[1]
} else if len(os.Args[1:]) == 2 {
addr = fmt.Sprintf("%s:%s", os.Args[1], os.Args[2])
addr = os.Args[1]
if _serveJson, err := strconv.ParseBool(os.Args[2]); err == nil {
serveJson = _serveJson
} else {
log.Fatal(err)
}
} else {
log.Panic(HelpText)
return
log.Fatal(HelpText)
}

handler := http.HandlerFunc(handleAny)
var handler http.Handler
if serveJson {
handler = http.HandlerFunc(handleAnyJson)
} else {
handler = http.HandlerFunc(handleAny)
}
log.Fatal(http.ListenAndServe(addr, handler))
}