crawl: write WARC to ISO 28500 - #98
Merged
Merged
Conversation
The crawl side had a WARC writer in name only. It wrote a response record with five headers, no digests, no request record, no warcinfo, and a Content-Length copied off the wire, and it was not reachable from any command. Nothing that reads WARC for a living could check that file against itself. This replaces it with a WARCWriter that writes what the standard asks for. Each file opens with a warcinfo record naming the tool and the command, and every record in the file carries that record's ID. A fetch becomes a request and a response record linked both ways with WARC-Concurrent-To, written together so a rotation never splits a pair. Every record gets a WARC-Block-Digest and every response a WARC-Payload-Digest over the HTTP body, sha1 and base32. The address the server answered from goes in WARC-IP-Address, captured with httptrace because that is the only place it is visible. CrawlURL now keeps what a record needs: the request and response header blocks, the remote address, and whether the body cap cut the response short, which becomes WARC-Truncated: length instead of a silently short page. The headers are rebuilt rather than captured, because net/http hands back a decoded body and a parsed header and never the wire bytes. That makes rewriting Content-Length mandatory and makes dropping a Transfer-Encoding or a decoded Content-Encoding mandatory too: the record has to describe the body stored with it. crawl fetch --warc-dir makes it reachable. Files take the first sequence number the directory does not already hold, so a second fetch adds rather than overwrites. Checked against warcio, which did not write any of this: 10 live sites into a rotating writer, 3 files, 20 records, every digest passing warcio check, 10 complete pairs with no partner in another file and no record carrying the wrong warcinfo ID. Ten single fetches through the CLI pass too, with every payload digest and Content-Length recomputed against the stored body.
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #53.
The crawl side had a WARC writer in name only: a response record with five headers, no digests, no request record, no warcinfo, a Content-Length copied off the wire, and no command that could reach it. It was WARC shaped and nothing else.
What is here
ccrawl/warcwrite.goholds aWARCWriterthat writes what the standard asks for.WARC-Warcinfo-ID.WARC-Concurrent-Toand written as one unit, so a rotation never splits a pair across files.WARC-Block-Digestand every response also aWARC-Payload-Digestover the HTTP body, bothsha1:and base32.WARC-IP-Addresscomes from an httptrace hook, which is the only place the remote address is observable, and the last connection in a redirect chain is the one that ends up in the record.WARC-Truncated: lengthrather than storing a short page as if it were the whole thing.CrawlURLkeeps the request and response header blocks, the remote address and the truncation flag. Those headers are rebuilt rather than captured, because net/http hands back a decoded body and a parsed header and never the wire bytes. That is what makes the header rewriting mandatory:Content-Lengthis always recomputed against the stored body,Transfer-Encodingis always dropped since Go dechunks on the way in, andContent-Encodingis dropped when we decoded it. The record has to describe the body stored with it, and it does.crawl fetch --warc-dirmakes it reachable from the CLI and reports the path it wrote.Checked against a tool that did not write it
warcio, ten live sites, one rotating writer:
Ten separate CLI fetches, one file each, all ten pass
warcio checkwith both digests verified per file. Reading them back with warcio and recomputing gives10 response payloads verified, 0 mismatched, where verified means the payload digest matches a fresh sha1 of the body and the storedContent-Lengthequals the stored body length. That set deliberately includes a gzipped response and a chunked one, which are the two cases the old writer could not have got right.Records round trip through
ccrawl parse: all three types come back with their URLs, statuses and payload digests intact.TestWARCWriterAgainstRealSitesis the live check, skipped unlessCCRAWL_WARC_OUTpoints somewhere, in the same shape as the robots corpus test.Removed
NewWARCRecordandWriteWARCResponse, with their two test call sites and the docs bullet that advertised them.Gates
go test ./...green,golangci-lint run ./...0 issues,scripts/docs-drift.shclean.