2011-10-21 9 views
5

Fondamentalmente le mie intestazioni di risposta contengonoCome posso inviare rimorchi/piè di pagina Http in una risposta chunked all'interno di un servlet java?

Transfer-encoding = chunked,

Trailer = [qualche rimorchio Voglio inviare dire ad esempio "SomeTrailer"]

Una volta che ho finito di scrivere i dati al Output output Servlet, Sto scrivendo il trailer "SomeTrailer: [value]", ma questo non viene analizzato correttamente dal httpclient. httpclient considera l'intero inputstream (incluso il trailer) come un singolo blocco . Ho anche provato a scrivere il trailer in un'intestazione di risposta dopo che i dati sono stati scritti nell'output, ma senza successo.

Si prega di aiutare

non ho trovato alcun buone fonti su questo.

+1

È veramente richiesto dal cliente? 'TE' e' Trailer' sono usati molto raramente, non l'ho mai visto nel vero codice mondiale. L'API servlet ha incorporato il supporto per le risposte chunked (ancora di più, lo invia per impostazione predefinita quando non si imposta la lunghezza del contenuto della risposta). Ma non ha il supporto integrato per rimorchi di risposta chunked. In alternativa, è possibile impostare il valore del trailer desiderato come intestazione di risposta personalizzata (solo se il valore è compatibile US-ASCII e non supera una determinata lunghezza massima). – BalusC

+0

@BalusC Il mio caso d'uso è che il server sta inviando un flusso infinito di dati e non conosce la lunghezza del suo contenuto. Il codice del server sta anche calcolando il checksum in modo che possa inviarlo come trailer, che il client utilizzerà per verificare i dati. –

risposta

3

Ho finito per scrivere un semplice server web a thread singolo per questo. Alla fine è stato abbastanza facile. Il server è piuttosto semplice. Il codice è un po 'approssimativo, ma l'idea principale è lì.

Che cosa invia i filecontents come prima parte e il checksum del file come footer.

import java.io.BufferedReader; 
import java.io.DataOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.InputStreamReader; 
import java.net.ServerSocket; 
import java.net.Socket; 

import org.apache.commons.codec.digest.DigestUtils; 
import org.apache.commons.io.IOUtils; 
import org.apache.log4j.Logger; 

public class ChunkedResponseServer implements Runnable { 
    private static final Logger LOGGER = Logger.getLogger(ChunkedResponseServer.class); 

    // Space ' ' 
    static final byte SP = 32; 
    // Tab ' ' 
    static final byte HT = 9; 
    // Carriage return 
    static final byte CR = 13; 
    // Line feed character 
    static final byte LF = 10; 

    final int port; 

    private volatile boolean cancelled = false; 

    public ChunkedResponseServer(int port) { 
    LOGGER.info("Chunked response server running on port " + port); 
    this.port = port; 
    } 

    @Override 
    public void run() { 
    ServerSocket serverSocket = null; 
    try { 
     serverSocket = new ServerSocket(port); 
     while (!cancelled) { 
     final Socket connectionSocket = serverSocket.accept(); 
     handle(connectionSocket); 
     } 
    } catch (final IOException e) { 
     throw new RuntimeException(e); 
    } 
    } 

    public void cancel() { 
    LOGGER.info("Shutting down Chunked response Server"); 
    cancelled = true; 
    } 

    private void handle(Socket socket) throws IOException { 
    BufferedReader input = null; 
    DataOutputStream output = null; 
    try { 
     input = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
     output = new DataOutputStream(socket.getOutputStream()); 

     addHeaders(output); 
     addCRLR(output); 

     final String filename = readFilename(input); 
     final byte[] content = readContent(filename); 
     addContentAsChunk(output, content); 

     final String checksum = DigestUtils.md5Hex(content); 
     addLastChunkAndChecksumFooter(output, checksum); 
     addCRLR(output); 

    } finally { 
     IOUtils.closeQuietly(input); 
     IOUtils.closeQuietly(output); 
    } 
    } 

    private void addLastChunkAndChecksumFooter(DataOutputStream output, String checksum) throws IOException { 
    output.writeBytes("0"); 
    addCRLR(output); 
    output.writeBytes("checksum: " + checksum); 
    addCRLR(output); 
    } 

    private void addContentAsChunk(DataOutputStream output, byte[] content) throws IOException { 
    output.writeBytes(Integer.toHexString(content.length)); 
    addCRLR(output); 
    output.write(content); 
    addCRLR(output); 
    } 

    private void addCRLR(DataOutputStream output) throws IOException { 
    output.writeByte(CR); 
    output.writeByte(LF); 
    } 

    private void addHeaders(DataOutputStream output) throws IOException { 
    output.writeBytes("HTTP/1.1 200 OK"); 
    addCRLR(output); 
    output.writeBytes("Content-type: text/plain"); 
    addCRLR(output); 
    output.writeBytes("Transfer-encoding: chunked"); 
    addCRLR(output); 
    output.writeBytes("Trailer: checksum"); 
    addCRLR(output); 
    } 

    private String readFilename(BufferedReader input) throws IOException { 
    final String initialLine = input.readLine(); 
    final String filePath = initialLine.split(" ")[1]; 
    final String[] components = filePath.split("/"); 
    return components[components.length - 1]; 
    } 

    private byte[] readContent(String filename) throws IOException { 
    final InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(filename); 
    return IOUtils.toByteArray(in); 
    } 
}