All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netpreserve.jwarc.Message Maven / Gradle / Ivy

The newest version!
/*
 * SPDX-License-Identifier: Apache-2.0
 * Copyright (C) 2018 National Library of Australia and the jwarc contributors
 */

package org.netpreserve.jwarc;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * A message consisting of headers and a content block (body). Forms the basis of protocols and formats like HTTP and
 * WARC.
 */
public abstract class Message {
    private final MessageVersion version;
    private final MessageHeaders headers;
    private final MessageBody body;
    byte[] serializedHeader;

    Message(MessageVersion version, MessageHeaders headers, MessageBody body) {
        this.version = version;
        this.headers = headers;
        this.body = body;
    }

    /**
     * The named header fields of this message.
     */
    public MessageHeaders headers() {
        return headers;
    }

    /**
     * The content body of this message.
     */
    public MessageBody body() {
        return body;
    }

    /**
     * The version of the network protocol or file format containing this message.
     */
    public MessageVersion version() {
        return version;
    }

    /**
     * The media type of the body.
     * 

* Returns "application/octet-stream" if the Content-Type header is missing. */ public MediaType contentType() { return headers.first("Content-Type").map(MediaType::parseLeniently).orElse(MediaType.OCTET_STREAM); } void serializeHeaderTo(Appendable output) throws IOException { output.append(version().toString()); output.append("\r\n"); headers.appendTo(output); output.append("\r\n"); } Charset headerCharset() { return StandardCharsets.UTF_8; } /** * Serializes the message header. */ public byte[] serializeHeader() { if (serializedHeader == null) { try { StringBuilder sb = new StringBuilder(); serializeHeaderTo(sb); serializedHeader = sb.toString().getBytes(headerCharset()); } catch (IOException e) { throw new UncheckedIOException(e); } } return serializedHeader; } @SuppressWarnings("unchecked") public static abstract class AbstractBuilder> { protected Map> headerMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); protected MessageVersion version; protected ReadableByteChannel bodyChannel; public AbstractBuilder(MessageVersion defaultVersion) { this.version = defaultVersion; } /** * Finishes building. */ public abstract R build(); /** * Adds a header field. Existing headers with the same name are not removed. */ public B addHeader(String name, String value) { Objects.requireNonNull(name, "name"); Objects.requireNonNull(value, "value"); headerMap.computeIfAbsent(name, n -> new ArrayList<>()).add(value); return (B) this; } /** * Appends header fields. Existing headers with the same name are not removed. */ public B addHeaders(Map> headers) { for (Map.Entry> entry : headers.entrySet()) { for (String value : entry.getValue()) { addHeader(entry.getKey(), value); } } return (B) this; } /** * Sets a header field. Removes any existing headers with the same name. If value is null then * simply removes all existing headers with the given name. */ public B setHeader(String name, String value) { Objects.requireNonNull(name); if (value == null) { headerMap.remove(name); } else { List list = new ArrayList<>(); list.add(value); headerMap.put(name, list); } return (B) this; } /** * Sets the protocol version of this message or record. */ public B version(MessageVersion version) { this.version = version; return (B)this; } /** * Sets the message body. The Content-Length header will be set to the length of contentBytes. * The Content-Type header will be set to contentType unless it is null. */ public B body(MediaType contentType, byte[] contentBytes) { return body(contentType, Channels.newChannel(new ByteArrayInputStream(contentBytes)), contentBytes.length); } /** * Sets the message body. The Content-Length header will be set to length. * The Content-Type header will be set to contentType unless it is null. */ public B body(MediaType contentType, ReadableByteChannel channel, long length) { if (contentType != null) { setHeader("Content-Type", contentType.toString()); } setHeader("Content-Length", Long.toString(length)); this.bodyChannel = channel; return (B) this; } MessageBody makeBody() { long contentLength = 0; List values = headerMap.get("Content-Length"); if (values != null && !values.isEmpty()) { contentLength = Long.parseLong(values.get(0)); } return LengthedBody.create(bodyChannel, ByteBuffer.allocate(0), contentLength); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy