Skip to content

Bind pure-Go webrick into rbgo (require "webrick") - #201

Closed
tannevaled wants to merge 1 commit into
mainfrom
feat/bind-webrick
Closed

tannevaled wants to merge 1 commit into
mainfrom
feat/bind-webrick

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Binds github.com/go-ruby-webrick/webrick — the pure-Go (CGO=0) port of Ruby's WEBrick HTTP server core — into rbgo so require "webrick" works with no .rb file.

Surface

The library owns the deterministic request parse, response build, HTTPStatus table and longest-prefix mount dispatch, and does no socket I/O (the TCP accept loop is a host seam). This shell maps that onto Ruby classes:

  • WEBrick::HTTPServer — .new(Port:, ServerName:, ServerSoftware:), #mount_proc(dir){ |req,res| … }, #mount(dir, servlet) (an AbstractServlet subclass instantiated per request, or a pre-built instance), #unmount/#umount, #service(req, res), #config. #start/#run/#shutdown/#stop are the host accept-loop seam — no-ops here.
  • WEBrick::HTTPRequest — .new(config=nil), #parse(str), and the parsed accessors: request_method, unparsed_uri, path, script_name, path_info, query_string, http_version, host, port, keep_alive?, content_length, content_type, body, query, [], each.
  • WEBrick::HTTPResponse — .new(config=nil), #request=, status/status=, reason_phrase, status_line, []/[]=, content_type/content_type=, content_length=, body/body=, chunked?/chunked=, keep_alive?, #set_redirect, #set_error, #to_s (the wire bytes).
  • WEBrick::HTTPServlet::AbstractServlet — the do_<METHOD> servlet base class.
  • WEBrick::HTTPStatus — reason_phrase, the range predicates (info?…server_error?), and the Status exception family (each named leaf carrying its code, with #code/#to_i/#reason_phrase).

Request-handler seam

A mount_proc block or a servlet's do_<METHOD> method is the injectable Ruby handler. Because the core does no I/O and starts no goroutines, #service is driven on the VM goroutine while it holds the GVL, so the handler runs inline (vm.callBlock / vm.send) with no extra goroutine and nothing to leak. A handler that raises a WEBrick::HTTPStatus unwinds as a Go panic through the library's dispatch back to webrickService, which recovers it and renders the matching error response — exactly as WEBrick::HTTPServer#run rescues HTTPStatus::Status. A servlet is dispatched through a library AbstractServlet, so HEAD→GET, the OPTIONS Allow list and MethodNotAllowed come from the library, over the Ruby methods.

Tests / verification

Tests drive requests through the server object in-process (build a HTTPRequest, dispatch via #service, assert the HTTPResponse) — no listening socket, no port bind.

  • Build 0; go vet 0; gofmt -l clean.
  • 100% coverage of the new files (webrick.go, webrick_bind.go).
  • Full ./... suite green; -race clean (no goroutine leaks).
  • 6-arch GOOS=linux build green: amd64, arm64, riscv64, loong64, ppc64le, s390x.

🤖 Generated with Claude Code

Bind github.com/go-ruby-webrick/webrick — the pure-Go (CGO=0) port of
Ruby's WEBrick HTTP server core — into rbgo so `require "webrick"` works
with no .rb file.

The library owns the deterministic request parse, response build, status
table and longest-prefix mount dispatch and does no socket I/O; this shell
maps that surface onto Ruby classes:

- WEBrick::HTTPServer.new(config) + #mount_proc { |req, res| … } / #mount
  (AbstractServlet subclass or instance) / #unmount / #service / #config
  (#start/#shutdown/#stop are the host accept-loop seam, no-ops here).
- WEBrick::HTTPRequest.new + #parse(str) and the parsed accessors
  (request_method / path / query / headers / keep_alive? / body / each …).
- WEBrick::HTTPResponse.new with the status/header/body accessors,
  #set_redirect, #set_error and #to_s (the wire bytes).
- WEBrick::HTTPServlet::AbstractServlet base class (do_<METHOD> contract).
- WEBrick::HTTPStatus: reason_phrase, the range predicates, and the Status
  exception family (each named leaf carrying its code).

The request-handler seam — a mount_proc block or a servlet's do_<METHOD>
method — is the injectable Ruby code the engine leaves open. Because the
core does no I/O and starts no goroutines, #service is driven on the VM
goroutine under the GVL and the handler runs inline (vm.callBlock /
vm.send), so nothing leaks; a handler that raises a WEBrick::HTTPStatus is
recovered and rendered as the matching error response, exactly as
WEBrick::HTTPServer#run rescues HTTPStatus::Status.

Tests drive requests through the server object in-process (build a
HTTPRequest, dispatch via #service, assert the HTTPResponse) with no
listening socket. 100% coverage of the new files; -race clean; green on
all six 64-bit arches.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tannevaled

Copy link
Copy Markdown
Contributor Author

Superseded by the net-wave consolidation PR (single merge onto main; shared builtins/require/object_model). Work preserved verbatim; branch kept.

@tannevaled tannevaled closed this Jul 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant