Task 281: .HAR File Format

Task 281: .HAR File Format

1. List of All Properties Intrinsic to the .HAR File Format

The .HAR (HTTP Archive) format is a JSON-based structure with no binary headers, magic numbers, or traditional file system metadata (e.g., no embedded files, compression layers, or directory structures like ZIP). It is a plain UTF-8 text file containing a single JSON object. The "intrinsic properties" refer to the defined fields in the JSON schema, which form the hierarchical structure for logging HTTP data. Below is a comprehensive list of all properties from the HAR 1.2 specification, grouped by object for clarity. Each includes type and brief description. Optional properties are marked.

Root log Object (Required)

  • version (string, required): Format version (e.g., "1.2").
  • creator (object, required): Creator application details.
  • name (string, required): Application name.
  • version (string, required): Application version.
  • comment (string, optional): Comment.
  • browser (object, optional): Browser details.
  • name (string, required if present): Browser name.
  • version (string, required if present): Browser version.
  • comment (string, optional): Comment.
  • pages (array of page objects, optional): List of tracked pages.
  • entries (array of entry objects, required): List of HTTP requests.
  • comment (string, optional): Overall comment.

page Object (Within pages)

  • startedDateTime (string, required): Page load start time (ISO 8601).
  • id (string, required): Unique page ID.
  • title (string, required): Page title.
  • pageTimings (object, required): Page load timings.
  • onContentLoad (number, optional): Time to content load (ms, -1 if N/A).
  • onLoad (number, optional): Time to full load (ms, -1 if N/A).
  • comment (string, optional): Comment.
  • comment (string, optional): Comment.

entry Object (Within entries)

  • pageref (string, optional): Reference to parent page ID.
  • startedDateTime (string, required): Request start time (ISO 8601).
  • time (number, required): Total request time (ms).
  • request (object, required): Request details.
  • method (string, required): HTTP method (e.g., "GET").
  • url (string, required): Full URL.
  • httpVersion (string, required): HTTP version (e.g., "HTTP/1.1").
  • cookies (array of cookie objects, optional): Request cookies.
  • headers (array of header objects, optional): Request headers.
  • queryString (array of queryParam objects, optional): Query parameters.
  • postData (object, optional): POST data.
  • headersSize (number, required): Request headers size (bytes, -1 if N/A).
  • bodySize (number, required): Request body size (bytes, -1 if N/A).
  • comment (string, optional): Comment.
  • response (object, required): Response details.
  • status (number, required): HTTP status code.
  • statusText (string, required): Status text.
  • httpVersion (string, required): HTTP version.
  • cookies (array of cookie objects, optional): Response cookies.
  • headers (array of header objects, optional): Response headers.
  • content (object, required): Response content.
  • redirectURL (string, required): Redirect URL.
  • headersSize (number, required): Response headers size (bytes, -1 if N/A).
  • bodySize (number, required): Response body size (bytes, -1 if N/A).
  • comment (string, optional): Comment.
  • cache (object, optional): Cache info.
  • beforeRequest (object, optional): Cache state before request.
  • expires (string, optional): Expiration time (ISO 8601).
  • lastAccess (string, optional): Last access time (ISO 8601).
  • eTag (string, optional): ETag.
  • hitCount (number, optional): Hit count.
  • size (number, optional): Size (bytes).
  • afterResponse (object, optional): Cache state after response (same subproperties as beforeRequest).
  • comment (string, optional): Comment.
  • timings (object, required): Timing phases (ms, -1 if N/A).
  • blocked (number, optional): Queue time.
  • dns (number, optional): DNS resolution time.
  • connect (number, optional): Connection time.
  • send (number, optional): Send time.
  • wait (number, optional): Wait time.
  • receive (number, optional): Receive time.
  • ssl (number, optional): SSL negotiation time.
  • comment (string, optional): Comment.
  • serverIPAddress (string, optional): Server IP.
  • connection (string, optional): Connection ID.
  • comment (string, optional): Comment.

Common Sub-Objects

  • cookie (object):
  • name (string, required): Cookie name.
  • value (string, required): Cookie value.
  • path (string, optional): Path.
  • domain (string, optional): Domain.
  • expires (string, optional): Expiration (ISO 8601).
  • httpOnly (boolean, optional): HTTP-only flag.
  • secure (boolean, optional): Secure flag.
  • comment (string, optional): Comment.
  • header (object):
  • name (string, required): Header name.
  • value (string, required): Header value.
  • comment (string, optional): Comment.
  • queryParam (object):
  • name (string, required): Parameter name.
  • value (string, required): Parameter value.
  • comment (string, optional): Comment.
  • postData (object, within request):
  • mimeType (string, required if present): MIME type.
  • params (array of {name: string, value: string, comment?: string}, optional): Parameters.
  • text (string, optional): Raw text (mutually exclusive with params).
  • comment (string, optional): Comment.
  • content (object, within response):
  • size (number, required): Content size (bytes).
  • compression (number, optional): Compression savings (bytes).
  • mimeType (string, required): MIME type.
  • text (string, optional): Response text (decoded or encoded).
  • encoding (string, optional): Text encoding (e.g., "base64").
  • comment (string, optional): Comment.

3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .HAR Parsing

Embed this as a custom HTML card in Ghost (via the editor's HTML block). It creates a drag-and-drop zone that parses the uploaded .HAR file (JSON) and dumps all properties recursively to a <pre> element on screen.

Drag and drop a .HAR file here to view properties



4. Python Class for .HAR Handling

This class reads a .HAR file, decodes (parses JSON), prints all properties recursively to console, and supports writing back to a new .HAR file.

import json
import sys

class HARHandler:
    def __init__(self, filepath=None):
        self.data = None
        if filepath:
            self.read(filepath)

    def read(self, filepath):
        """Read and decode .HAR file."""
        with open(filepath, 'r', encoding='utf-8') as f:
            self.data = json.load(f)
        self._print_properties(self.data, 'log')

    def write(self, filepath):
        """Write data back to .HAR file."""
        if not self.data:
            raise ValueError("No data to write.")
        with open(filepath, 'w', encoding='utf-8') as f:
            json.dump(self.data, f, indent=2, ensure_ascii=False)

    def _print_properties(self, obj, path=''):
        """Recursively print all properties to console."""
        if isinstance(obj, dict):
            for key, value in obj.items():
                new_path = f"{path}.{key}" if path else key
                print(f"{new_path}: {value}")
                self._print_properties(value, new_path)
        elif isinstance(obj, list):
            for idx, item in enumerate(obj):
                new_path = f"{path}[{idx}]"
                print(f"{new_path}: {type(item).__name__} (array item)")
                self._print_properties(item, new_path)
        else:
            print(f"{path}: {obj}")

# Example usage:
# handler = HARHandler('sample.har')
# handler.write('output.har')

5. Java Class for .HAR Handling

This class uses Jackson library (add com.fasterxml.jackson.core:jackson-databind:2.15.0 to your project). It reads, decodes, prints properties recursively to console, and writes back.

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class HARHandler {
    private JsonNode data;

    public HARHandler(String filepath) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        this.data = mapper.readTree(new File(filepath));
        printProperties(data, "log");
    }

    public void write(String filepath) throws IOException {
        if (data == null) throw new IllegalStateException("No data to write.");
        ObjectMapper mapper = new ObjectMapper();
        mapper.writeValue(new File(filepath), data);
    }

    private void printProperties(JsonNode node, String path) {
        if (node.isObject()) {
            node.fields().forEachRemaining(entry -> {
                String key = entry.getKey();
                JsonNode value = entry.getValue();
                String newPath = path.isEmpty() ? key : path + "." + key;
                System.out.println(newPath + ": " + value);
                printProperties(value, newPath);
            });
        } else if (node.isArray()) {
            for (int i = 0; i < node.size(); i++) {
                String newPath = path + "[" + i + "]";
                System.out.println(newPath + ": " + node.get(i).getNodeType() + " (array item)");
                printProperties(node.get(i), newPath);
            }
        } else {
            System.out.println(path + ": " + node.asText());
        }
    }

    public static void main(String[] args) throws IOException {
        if (args.length > 0) {
            HARHandler handler = new HARHandler(args[0]);
            // handler.write("output.har");
        }
    }
}

6. JavaScript Class for .HAR Handling

This Node.js class (uses fs module) reads, decodes, prints properties recursively to console, and writes back. Run with Node.js.

const fs = require('fs');

class HARHandler {
  constructor(filepath = null) {
    this.data = null;
    if (filepath) {
      this.read(filepath);
    }
  }

  read(filepath) {
    const content = fs.readFileSync(filepath, 'utf8');
    this.data = JSON.parse(content);
    this._printProperties(this.data, 'log');
  }

  write(filepath) {
    if (!this.data) throw new Error('No data to write.');
    fs.writeFileSync(filepath, JSON.stringify(this.data, null, 2), 'utf8');
  }

  _printProperties(obj, path = '') {
    if (obj === null || typeof obj !== 'object') {
      console.log(`${path}: ${obj}`);
      return;
    }
    if (Array.isArray(obj)) {
      obj.forEach((item, idx) => {
        const newPath = `${path}[${idx}]`;
        console.log(`${newPath}: ${typeof item} (array item)`);
        this._printProperties(item, newPath);
      });
    } else {
      Object.entries(obj).forEach(([key, value]) => {
        const newPath = path ? `${path}.${key}` : key;
        console.log(`${newPath}: ${value}`);
        this._printProperties(value, newPath);
      });
    }
  }
}

// Example usage:
// new HARHandler('sample.har');
// handler.write('output.har');

7. C Class (Struct) for .HAR Handling

This uses the cJSON library (download from https://github.com/DaveGamble/cJSON). Compile with: gcc -o har_handler har_handler.c -lcjson. It reads, decodes (parses JSON), prints properties recursively to stdout, and writes back.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"

typedef struct {
    cJSON *data;
} HARHandler;

HARHandler *har_handler_new(const char *filepath) {
    FILE *f = fopen(filepath, "r");
    if (!f) return NULL;
    fseek(f, 0, SEEK_END);
    long len = ftell(f);
    fseek(f, 0, SEEK_SET);
    char *buf = malloc(len + 1);
    fread(buf, 1, len, f);
    buf[len] = '\0';
    fclose(f);

    HARHandler *handler = malloc(sizeof(HARHandler));
    handler->data = cJSON_Parse(buf);
    free(buf);
    if (handler->data) {
        print_properties(handler->data, "log");
    }
    return handler;
}

void har_handler_free(HARHandler *handler) {
    if (handler) {
        cJSON_Delete(handler->data);
        free(handler);
    }
}

void write_har(HARHandler *handler, const char *filepath) {
    if (!handler || !handler->data) return;
    char *json_str = cJSON_Print(handler->data);
    FILE *f = fopen(filepath, "w");
    if (f) {
        fputs(json_str, f);
        fclose(f);
    }
    free(json_str);
}

void print_properties(cJSON *node, const char *path) {
    if (!node) return;
    if (cJSON_IsObject(node)) {
        cJSON *child;
        cJSON_ArrayForEach(child, node) {
            char new_path[1024];
            snprintf(new_path, sizeof(new_path), "%s%s%s", path, strlen(path) ? "." : "", child->string);
            printf("%s: %s\n", new_path, cJSON_Print(child));
            print_properties(child, new_path);
        }
    } else if (cJSON_IsArray(node)) {
        int i = 0;
        cJSON *child;
        cJSON_ArrayForEach(child, node) {
            char new_path[1024];
            snprintf(new_path, sizeof(new_path), "%s[%d]", path, i++);
            printf("%s: %s (array item)\n", new_path, cJSON_GetStringValue(child) ? cJSON_GetStringValue(child) : "object");
            print_properties(child, new_path);
        }
    } else {
        printf("%s: %s\n", path, cJSON_PrintUnformatted(node));
    }
}

int main(int argc, char **argv) {
    if (argc > 1) {
        HARHandler *handler = har_handler_new(argv[1]);
        if (handler) {
            // write_har(handler, "output.har");
            har_handler_free(handler);
        }
    }
    return 0;
}