From 4d352a0ffa4719ff82561dad35d144f28d5055d8 Mon Sep 17 00:00:00 2001 From: andrewlalis Date: Wed, 5 Mar 2025 20:13:45 -0500 Subject: [PATCH] Upgraded dependencies, and simplified response output stream. --- .gitea/workflows/ci.yaml | 19 +++++++ dub.json | 4 +- dub.selections.json | 2 +- source/handy_http_transport/http1/transport.d | 22 ++++---- .../response_output_stream.d | 51 ++++++++++++++----- 5 files changed, 71 insertions(+), 27 deletions(-) create mode 100644 .gitea/workflows/ci.yaml diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml new file mode 100644 index 0000000..19ce754 --- /dev/null +++ b/.gitea/workflows/ci.yaml @@ -0,0 +1,19 @@ +name: Build and Test Module +on: + push: + paths: + - 'source/**' + - '.gitea/workflows/ci.yaml' + pull_request: + types: [opened, reopened, synchronize] +jobs: + build-and-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup DLang + uses: dlang-community/setup-dlang@v2 + with: + compiler: ldc-latest + - name: Build and Test + run: dub -q test diff --git a/dub.json b/dub.json index 94c3dd8..8b976fe 100644 --- a/dub.json +++ b/dub.json @@ -4,9 +4,9 @@ ], "copyright": "Copyright © 2024, Andrew Lalis", "dependencies": { - "handy-http-primitives": "~>1.0.0", + "handy-http-primitives": "~>1", "photon": "~>0.10.2", - "streams": "~>3.5.0" + "streams": "~>3" }, "description": "Implementations of HTTP transport protocols.", "license": "CC0", diff --git a/dub.selections.json b/dub.selections.json index 1e52fe2..f849e4f 100644 --- a/dub.selections.json +++ b/dub.selections.json @@ -1,7 +1,7 @@ { "fileVersion": 1, "versions": { - "handy-http-primitives": "1.0.0", + "handy-http-primitives": "1.2.0", "photon": "0.10.2", "sharded-map": "2.7.0", "streams": "3.5.0" diff --git a/source/handy_http_transport/http1/transport.d b/source/handy_http_transport/http1/transport.d index aef01fc..1e8d1d7 100644 --- a/source/handy_http_transport/http1/transport.d +++ b/source/handy_http_transport/http1/transport.d @@ -201,15 +201,15 @@ Either!(string[][string], "headers", StreamError, "error") parseHeaders(S)(S inp return Either!(string[][string], "headers", StreamError, "error")(headers); } -unittest { - class TestHandler : HttpRequestHandler { - void handle(ref ServerHttpRequest request, ref ServerHttpResponse response) { - response.status = HttpStatus.OK; - response.headers.add("Content-Type", "application/json"); - response.outputStream.writeToStream(cast(ubyte[]) "{\"a\": 1}"); - } - } +// unittest { +// class TestHandler : HttpRequestHandler { +// void handle(ref ServerHttpRequest request, ref ServerHttpResponse response) { +// response.status = HttpStatus.OK; +// response.headers.add("Content-Type", "application/json"); +// response.outputStream.writeToStream(cast(ubyte[]) "{\"a\": 1}"); +// } +// } - HttpTransport tp = new Http1Transport(new TestHandler(), 8080); - tp.start(); -} +// HttpTransport tp = new Http1Transport(new TestHandler(), 8080); +// tp.start(); +// } diff --git a/source/handy_http_transport/response_output_stream.d b/source/handy_http_transport/response_output_stream.d index 303d4b1..9278e97 100644 --- a/source/handy_http_transport/response_output_stream.d +++ b/source/handy_http_transport/response_output_stream.d @@ -50,28 +50,53 @@ struct HttpResponseOutputStream(S) if (isByteOutputStream!S) { * Returns: The stream result of writing. */ StreamResult writeHeaders() { - // TODO: Come up with a better way of writing headers than string concatenation. size_t idx = 0; char[6] statusCodeBuffer; // Normal HTTP codes are 3 digits, but this leaves room for extensions. writeUIntToBuffer(response.status.code, statusCodeBuffer, idx); - - string statusAndHeaders = "HTTP/1.1 " - ~ cast(string) statusCodeBuffer[0..idx] - ~ " " ~ response.status.text - ~ "\r\n"; + // Write the status line. + StreamResult r = outputStream.writeToStream(cast(ubyte[]) "HTTP/1.1 "); + if (r.hasError) return r; + size_t writeCount = r.count; + r = outputStream.writeToStream(cast(ubyte[]) statusCodeBuffer[0..idx]); + if (r.hasError) return r; + writeCount += r.count; + r = outputStream.writeToStream([' ']); + if (r.hasError) return r; + writeCount += r.count; + r = outputStream.writeToStream(cast(ubyte[]) response.status.text); + if (r.hasError) return r; + writeCount += r.count; + r = outputStream.writeToStream(['\r', '\n']); + if (r.hasError) return r; + writeCount += r.count; + foreach (headerName; response.headers.keys) { - string headerLine = headerName ~ ": "; + // Write the header name. + r = outputStream.writeToStream(cast(ubyte[]) headerName); + if (r.hasError) return r; + writeCount += r.count; + r = outputStream.writeToStream([':', ' ']); + if (r.hasError) return r; + writeCount += r.count; + // Write the comma-separated list of values. string[] headerValues = response.headers.getAll(headerName); for (size_t i = 0; i < headerValues.length; i++) { - headerLine ~= headerValues[i]; + r = outputStream.writeToStream(cast(ubyte[]) headerValues[i]); + if (r.hasError) return r; + writeCount += r.count; if (i + 1 < headerValues.length) { - headerLine ~= ", "; + r = outputStream.writeToStream([',', ' ']); + if (r.hasError) return r; + writeCount += r.count; } } - headerLine ~= "\r\n"; - statusAndHeaders ~= headerLine; + r = outputStream.writeToStream(['\r', '\n']); + if (r.hasError) return r; + writeCount += r.count; } - statusAndHeaders ~= "\r\n"; // Trailing CLRF before the body. - return outputStream.writeToStream(cast(ubyte[]) statusAndHeaders); + r = outputStream.writeToStream(['\r', '\n']); // Trailing CLRF before the body. + if (r.hasError) return r; + writeCount += r.count; + return StreamResult(cast(uint) writeCount); } }