/** @param sequenceNumber the index of this request on this connection. */ private RecordedRequest readRequest(InputStream in, int sequenceNumber) throws IOException { String request = readAsciiUntilCrlf(in); if (request.equals("")) { return null; // end of data; no more requests } List<String> headers = new ArrayList<String>(); int contentLength = -1; boolean chunked = false; String header; while (!(header = readAsciiUntilCrlf(in)).equals("")) { headers.add(header); String lowercaseHeader = header.toLowerCase(); if (contentLength == -1 && lowercaseHeader.startsWith("content-length:")) { contentLength = Integer.parseInt(header.substring(15).trim()); } if (lowercaseHeader.startsWith("transfer-encoding:") && lowercaseHeader.substring(18).trim().equals("chunked")) { chunked = true; } } boolean hasBody = false; TruncatingOutputStream requestBody = new TruncatingOutputStream(); List<Integer> chunkSizes = new ArrayList<Integer>(); if (contentLength != -1) { hasBody = true; transfer(contentLength, in, requestBody); } else if (chunked) { hasBody = true; while (true) { int chunkSize = Integer.parseInt(readAsciiUntilCrlf(in).trim(), 16); if (chunkSize == 0) { readEmptyLine(in); break; } chunkSizes.add(chunkSize); transfer(chunkSize, in, requestBody); readEmptyLine(in); } } if (request.startsWith("GET ")) { if (hasBody) { throw new IllegalArgumentException("GET requests should not have a body!"); } } else if (request.startsWith("POST ")) { if (!hasBody) { throw new IllegalArgumentException("POST requests must have a body!"); } } else { throw new UnsupportedOperationException("Unexpected method: " + request); } return new RecordedRequest( request, headers, chunkSizes, requestBody.numBytesReceived, requestBody.toByteArray(), sequenceNumber); }
/** @param sequenceNumber the index of this request on this connection. */ private RecordedRequest readRequest( Socket socket, InputStream in, OutputStream out, int sequenceNumber) throws IOException { String request; try { request = readAsciiUntilCrlf(in); } catch (IOException streamIsClosed) { return null; // no request because we closed the stream } if (request.length() == 0) { return null; // no request because the stream is exhausted } List<String> headers = new ArrayList<String>(); long contentLength = -1; boolean chunked = false; boolean expectContinue = false; String header; while ((header = readAsciiUntilCrlf(in)).length() != 0) { headers.add(header); String lowercaseHeader = header.toLowerCase(Locale.US); if (contentLength == -1 && lowercaseHeader.startsWith("content-length:")) { contentLength = Long.parseLong(header.substring(15).trim()); } if (lowercaseHeader.startsWith("transfer-encoding:") && lowercaseHeader.substring(18).trim().equals("chunked")) { chunked = true; } if (lowercaseHeader.startsWith("expect:") && lowercaseHeader.substring(7).trim().equals("100-continue")) { expectContinue = true; } } if (expectContinue) { out.write(("HTTP/1.1 100 Continue\r\n").getBytes(Util.US_ASCII)); out.write(("Content-Length: 0\r\n").getBytes(Util.US_ASCII)); out.write(("\r\n").getBytes(Util.US_ASCII)); out.flush(); } boolean hasBody = false; TruncatingOutputStream requestBody = new TruncatingOutputStream(); List<Integer> chunkSizes = new ArrayList<Integer>(); if (contentLength != -1) { hasBody = true; transfer(contentLength, in, requestBody); } else if (chunked) { hasBody = true; while (true) { int chunkSize = Integer.parseInt(readAsciiUntilCrlf(in).trim(), 16); if (chunkSize == 0) { readEmptyLine(in); break; } chunkSizes.add(chunkSize); transfer(chunkSize, in, requestBody); readEmptyLine(in); } } if (request.startsWith("OPTIONS ") || request.startsWith("GET ") || request.startsWith("HEAD ") || request.startsWith("DELETE ") || request.startsWith("TRACE ") || request.startsWith("CONNECT ")) { if (hasBody) { throw new IllegalArgumentException("Request must not have a body: " + request); } } else if (!request.startsWith("POST ") && !request.startsWith("PUT ")) { throw new UnsupportedOperationException("Unexpected method: " + request); } return new RecordedRequest( request, headers, chunkSizes, requestBody.numBytesReceived, requestBody.toByteArray(), sequenceNumber, socket); }