001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027 028package org.apache.http.impl.entity; 029 030import org.apache.http.Header; 031import org.apache.http.HttpException; 032import org.apache.http.HttpMessage; 033import org.apache.http.HttpVersion; 034import org.apache.http.ProtocolException; 035import org.apache.http.annotation.ThreadingBehavior; 036import org.apache.http.annotation.Contract; 037import org.apache.http.entity.ContentLengthStrategy; 038import org.apache.http.protocol.HTTP; 039import org.apache.http.util.Args; 040 041/** 042 * The strict implementation of the content length strategy. This class 043 * will throw {@link ProtocolException} if it encounters an unsupported 044 * transfer encoding or a malformed {@code Content-Length} header 045 * value. 046 * <p> 047 * This class recognizes "chunked" and "identitiy" transfer-coding only. 048 * 049 * @since 4.0 050 */ 051@Contract(threading = ThreadingBehavior.IMMUTABLE) 052public class StrictContentLengthStrategy implements ContentLengthStrategy { 053 054 public static final StrictContentLengthStrategy INSTANCE = new StrictContentLengthStrategy(); 055 056 private final int implicitLen; 057 058 /** 059 * Creates {@code StrictContentLengthStrategy} instance with the given length used per default 060 * when content length is not explicitly specified in the message. 061 * 062 * @param implicitLen implicit content length. 063 * 064 * @since 4.2 065 */ 066 public StrictContentLengthStrategy(final int implicitLen) { 067 super(); 068 this.implicitLen = implicitLen; 069 } 070 071 /** 072 * Creates {@code StrictContentLengthStrategy} instance. {@link ContentLengthStrategy#IDENTITY} 073 * is used per default when content length is not explicitly specified in the message. 074 */ 075 public StrictContentLengthStrategy() { 076 this(IDENTITY); 077 } 078 079 @Override 080 public long determineLength(final HttpMessage message) throws HttpException { 081 Args.notNull(message, "HTTP message"); 082 // Although Transfer-Encoding is specified as a list, in practice 083 // it is either missing or has the single value "chunked". So we 084 // treat it as a single-valued header here. 085 final Header transferEncodingHeader = message.getFirstHeader(HTTP.TRANSFER_ENCODING); 086 if (transferEncodingHeader != null) { 087 final String s = transferEncodingHeader.getValue(); 088 if (HTTP.CHUNK_CODING.equalsIgnoreCase(s)) { 089 if (message.getProtocolVersion().lessEquals(HttpVersion.HTTP_1_0)) { 090 throw new ProtocolException( 091 "Chunked transfer encoding not allowed for " + 092 message.getProtocolVersion()); 093 } 094 return CHUNKED; 095 } else if (HTTP.IDENTITY_CODING.equalsIgnoreCase(s)) { 096 return IDENTITY; 097 } else { 098 throw new ProtocolException( 099 "Unsupported transfer encoding: " + s); 100 } 101 } 102 final Header contentLengthHeader = message.getFirstHeader(HTTP.CONTENT_LEN); 103 if (contentLengthHeader != null) { 104 final String s = contentLengthHeader.getValue(); 105 try { 106 final long len = Long.parseLong(s); 107 if (len < 0) { 108 throw new ProtocolException("Negative content length: " + s); 109 } 110 return len; 111 } catch (final NumberFormatException e) { 112 throw new ProtocolException("Invalid content length: " + s); 113 } 114 } 115 return this.implicitLen; 116 } 117 118}