Files
GCDWebServer/CLAUDE.md
cuong mac mini m2 fe0036eb7e Migrate MIME-type lookup to UTType (UniformTypeIdentifiers)
Replace the deprecated CoreServices UTI calls
(UTTypeCreatePreferredIdentifierForTag / UTTypeCopyPreferredTagWithClass)
in GCDWebServerGetMimeTypeForExtension with the modern
[UTType typeWithFilenameExtension:].preferredMIMEType, removing the
-Wdeprecated-declarations pragma. Behavior is unchanged: known
extensions map to the same MIME type, unknown ones fall back to the
default.

The UniformTypeIdentifiers framework (macOS 11+ / iOS 14+ / tvOS 14+,
within the project's deployment targets) is now linked in all three
build systems: Xcode (OTHER_LDFLAGS), CocoaPods (Core subspec
frameworks), and SwiftPM (linkedFramework).

Verified: Run-Tests.sh, swift build, and pod lib lint all pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 16:57:23 +07:00

8.7 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Overview

GCDWebServer is a lightweight, GCD-based HTTP 1.1 server written in Objective-C (ARC) for embedding in iOS, macOS, and tvOS apps. It has no third-party dependencies. This fork targets iOS 16 / tvOS 16 / macOS 12 (set in both GCDWebServer.xcodeproj and GCDWebServer.podspec).

Not supported by design: keep-alive connections and HTTPS.

Build & Test

The library can be built three ways: the Xcode project (GCDWebServer.xcodeproj), CocoaPods (GCDWebServer.podspec), and Swift Package Manager (Package.swift). All three compile the same source files in place — see "Swift Package Manager layout" below for how the SPM include/ directories relate to the real sources.

Build via SPM:

swift build              # builds GCDWebServer, GCDWebUploader, GCDWebDAVServer

Schemes (xcodebuild -list -project GCDWebServer.xcodeproj):

  • GCDWebServer (Mac|iOS|tvOS) — the static library / framework per platform.
  • GCDWebServers (Mac|iOS|tvOS) — the framework target plus the XCTest unit tests (Frameworks/Tests.m).
  • Build All.

Run the unit tests (XCTest):

xcodebuild test -scheme "GCDWebServers (Mac)"

Run a single XCTest method:

xcodebuild test -scheme "GCDWebServers (Mac)" -only-testing:Tests/Tests/testPaths

Full CI test suite (builds for all platforms at oldest + current deployment targets, then runs the request/response replay tests):

./Run-Tests.sh

Run-Tests.sh does the bulk of the real testing. It builds the GCDWebServer (Mac) target, then runs the resulting command-line GCDWebServer executable in test modes (webServer, webDAV, htmlForm, htmlFileUpload) against recorded fixtures.

This script was originally written for Xcode 11.3 and was updated for current Xcode/SDKs in this fork:

  • The hardcoded "oldest deployment target" builds now use 12.0 (macOS) / 16.0 (iOS) to match the project (the old 10.7/8.0 values fail on modern SDKs — no libarclite).
  • The webUploader fixture test is commented out: this fork customized GCDWebUploader (delete/move/create disabled in commit ef4b622), so the recorded Tests/WebUploader fixtures no longer match. Re-record them or re-enable the line if those features are restored.
  • The tvOS build step is commented out because it requires the tvOS Simulator runtime to be installed (xcodebuild -downloadPlatform tvOS); without it the storyboard compile fails with "tvOS … Platform Not Installed". Re-enable once the runtime is present.

A project adjustment was also needed for the suite to build under current Xcode: the Tests (Mac) target sets GENERATE_INFOPLIST_FILE = YES. MIME-type lookup in GCDWebServerFunctions.m uses the modern UTType API (UniformTypeIdentifiers), so that framework is linked in all three build systems (Xcode OTHER_LDFLAGS, the podspec Core subspec, and Package.swift).

How the fixture-replay tests work

The Tests/ directory holds recorded HTTP exchanges, not code. Each test case is a pair of files: NNN-<method>.request (raw bytes sent to the server) and NNN-<code>.response (expected raw response bytes). The test runner (-mode <mode> -root <payload-dir> -tests <test-dir>) replays each .request against a server rooted at an extracted copy of Tests/Payload.zip and byte-compares the reply to the .response. To add coverage for a new behavior, add a matching .request/.response pair to the relevant Tests/<Mode> directory rather than writing new XCTest code.

Formatting

Source style for Objective-C is defined by .clang-format (Google base style, ColumnLimit: 0). Format files with clang-format -style=file -i <files>. The Swift example apps use 2-space indentation (SwiftFormat).

Architecture

The entire library is built around 4 core classes in GCDWebServer/Core/:

  • GCDWebServer — owns the listening socket and the ordered list of handlers. Manages start/stop, options, Bonjour, NAT port mapping, and (on iOS) automatic suspend/resume across foreground/background transitions.
  • GCDWebServerConnection — one instance per open HTTP connection, lives until the connection closes. Drives reading the request and writing the response. Subclassable via hooks (it's exposed but not used directly).
  • GCDWebServerRequest — created once HTTP headers arrive; wraps the request and consumes the body. Subclasses in GCDWebServer/Requests/ choose how the body is handled (in memory, streamed to disk, parsed as a form, etc.).
  • GCDWebServerResponse — produced by a handler; wraps response headers and an optional body. Subclasses in GCDWebServer/Responses/ cover data-in-memory, file-on-disk, streamed, and error responses.

GCDWebServer/Core/GCDWebServerFunctions.{h,m} holds shared C helpers (e.g. GCDWebServerNormalizePath, MIME-type and date helpers). GCDWebServerPrivate.h is the internal/private header (including the logging-facility abstraction). GCDWebServerHTTPStatusCodes.h enumerates status codes.

Handler model

A request is matched and processed by handlers added to the server, evaluated in LIFO order (last added wins). Each handler is two GCD blocks:

  • GCDWebServerMatchBlock — runs as soon as headers are received; inspects method/URL/headers and returns a GCDWebServerRequest subclass instance to claim the request, or nil to pass.
  • GCDWebServerProcessBlock (sync) or GCDWebServerAsyncProcessBlock (async) — runs after the full request body is received and returns a GCDWebServerResponse (or nil/error response → 500).

Handlers run on arbitrary GCD threads, so handler code must be thread-safe and re-entrant.

Extensions (subclasses of GCDWebServer)

  • GCDWebUploader/GCDWebUploader, an HTML5 web UI for uploading/downloading/managing files in a directory. Ships front-end assets in GCDWebUploader.bundle.
  • GCDWebDAVServer/GCDWebDAVServer, a class-1 WebDAV server (partial class-2 for macOS Finder). Depends on libxml2.

Example apps & framework packaging

  • Mac/main.m — macOS command-line example (also serves as the test-runner executable).
  • iOS/, tvOS/ — Swift example apps (AppDelegate.swift, ViewController.swift).
  • Frameworks/ — umbrella header (GCDWebServers.h), module map, and the XCTest target source (Tests.m).

Swift Package Manager layout

SPM needs each target's public headers in a single directory, but the real headers live spread across GCDWebServer/Core, GCDWebServer/Requests, and GCDWebServer/Responses. To support SPM without moving any files (keeping the Xcode project and CocoaPods working), the public headers are surfaced through symlinks:

  • GCDWebServer/include/*.h are symlinks to the real public headers (everything except the private GCDWebServerPrivate.h, which is deliberately not linked so it stays out of the public module). GCDWebUploader/include/GCDWebUploader.h works the same way.
  • GCDWebServer/include/module.modulemap is hand-written with umbrella ".". This is required: if SPM auto-generated the module map it would pick include/GCDWebServer.h as an umbrella header (name matches the module) and only export what that one header imports, hiding the request/response subclasses. The umbrella directory exports them all.
  • All cross-header imports inside the library (including the framework umbrella Frameworks/GCDWebServers.h) use quoted form (#import "GCDWebServerRequest.h"), not framework-style (#import <GCDWebServers/...>); the quoted form is what lets the flat include/ symlink directory resolve them. Keep new imports quoted.
  • GCDWebUploader.bundle is a .copy resource. Because SPM nests it inside a generated module bundle, GCDWebUploader.m has a #if SWIFT_PACKAGE branch that finds it via SWIFTPM_MODULE_BUNDLE instead of bundleForClass:.

When adding or renaming a public header, add/remove the matching symlink in the relevant include/ directory or it won't be visible to SPM consumers. libxml2 (for WebDAV) needs no special SPM include flag — the macOS/iOS SDK ships a module map that resolves <libxml/parser.h> automatically.

Consumer linking requirements

When integrated manually (not via CocoaPods), consuming apps must link libz, and WebDAV additionally needs libxml2 with $(SDKROOT)/usr/include/libxml2 on the header search path. The GCDWebServer.podspec defines three subspecs: Core (default), WebDAV, and WebUploader.

Logging & debug builds

Define DEBUG=1 in GCC_PREPROCESSOR_DEFINITIONS to enable verbose logging plus internal consistency checks. Runtime verbosity is controlled via +[GCDWebServer setLogLevel:]. If XLFacility is present in the same Xcode project, GCDWebServer routes logging through it automatically (see GCDWebServerPrivate.h).