/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.jmeter.protocol.http.sampler;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.jmeter.protocol.http.util.HTTPConstants;
import org.apache.jmeter.samplers.SampleResult;
/**
* This is a specialisation of the SampleResult class for the HTTP protocol.
*
*/
public class HTTPSampleResult extends SampleResult {
private static final long serialVersionUID = 241L;
/** Set of all HTTP methods, that have no body */
private static final Set<String> METHODS_WITHOUT_BODY = new HashSet<>(
Arrays.asList(
HTTPConstants.HEAD,
HTTPConstants.OPTIONS,
HTTPConstants.TRACE));
private String cookies = ""; // never null
private String method;
/**
* The raw value of the Location: header; may be null.
* This is supposed to be an absolute URL:
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.30">RFC2616 sec14.30</a>
* but is often relative.
*/
private String redirectLocation;
private String queryString = ""; // never null
private static final String HTTP_NO_CONTENT_CODE = Integer.toString(HttpURLConnection.HTTP_NO_CONTENT);
private static final String HTTP_NO_CONTENT_MSG = "No Content"; // $NON-NLS-1$
public HTTPSampleResult() {
super();
}
public HTTPSampleResult(long elapsed) {
super(elapsed, true);
}
/**
* Construct a 'parent' result for an already-existing result, essentially
* cloning it
*
* @param res
* existing sample result
*/
public HTTPSampleResult(HTTPSampleResult res) {
super(res);
method=res.method;
cookies=res.cookies;
queryString=res.queryString;
redirectLocation=res.redirectLocation;
}
public void setHTTPMethod(String method) {
this.method = method;
}
public String getHTTPMethod() {
return method;
}
public void setRedirectLocation(String redirectLocation) {
this.redirectLocation = redirectLocation;
}
public String getRedirectLocation() {
return redirectLocation;
}
/**
* Determine whether this result is a redirect.
* Returns true for: 301,302,303 and 307(GET or HEAD)
* @return true iff res is an HTTP redirect response
*/
public boolean isRedirect() {
/*
* Don't redirect the following:
* 300 = Multiple choice
* 304 = Not Modified
* 305 = Use Proxy
* 306 = (Unused)
*/
final String[] REDIRECT_CODES = { HTTPConstants.SC_MOVED_PERMANENTLY,
HTTPConstants.SC_MOVED_TEMPORARILY,
HTTPConstants.SC_SEE_OTHER };
String code = getResponseCode();
for (String redirectCode : REDIRECT_CODES) {
if (redirectCode.equals(code)) {
return true;
}
}
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
// If the 307 status code is received in response to a request other than GET or HEAD,
// the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user,
// since this might change the conditions under which the request was issued.
// See Bug 54119
if (HTTPConstants.SC_TEMPORARY_REDIRECT.equals(code) &&
(HTTPConstants.GET.equals(getHTTPMethod()) || HTTPConstants.HEAD.equals(getHTTPMethod()))) {
return true;
}
return false;
}
/**
* Overrides version in Sampler data to provide more details
* <p>
* {@inheritDoc}
*/
@Override
public String getSamplerData() {
StringBuilder sb = new StringBuilder();
sb.append(method);
URL u = super.getURL();
if (u != null) {
sb.append(' ');
sb.append(u.toString());
sb.append('\n');
// Include request body if it can have one
if (!METHODS_WITHOUT_BODY.contains(method)) {
sb.append("\n").append(method).append(" data:\n");
sb.append(queryString);
sb.append('\n');
}
if (cookies.length()>0){
sb.append("\nCookie Data:\n");
sb.append(cookies);
} else {
sb.append("\n[no cookies]");
}
sb.append('\n');
}
final String sampData = super.getSamplerData();
if (sampData != null){
sb.append(sampData);
}
return sb.toString();
}
/**
* @return cookies as a string
*/
public String getCookies() {
return cookies;
}
/**
* @param string
* representing the cookies
*/
public void setCookies(String string) {
if (string == null) {
cookies="";// $NON-NLS-1$
} else {
cookies = string;
}
}
/**
* Fetch the query string
*
* @return the query string
*/
public String getQueryString() {
return queryString;
}
/**
* Save the query string
*
* @param string
* the query string
*/
public void setQueryString(String string) {
if (string == null ) {
queryString="";// $NON-NLS-1$
} else {
queryString = string;
}
}
/**
* Overrides the method from SampleResult - so the encoding can be extracted from
* the Meta content-type if necessary.
*
* Updates the dataEncoding field if the content-type is found.
* @param defaultEncoding Default encoding used if there is no data encoding
* @return the dataEncoding value as a String
*/
@Override
public String getDataEncodingWithDefault(String defaultEncoding) {
String dataEncodingNoDefault = getDataEncodingNoDefault();
if(dataEncodingNoDefault != null && dataEncodingNoDefault.length()> 0) {
return dataEncodingNoDefault;
}
return defaultEncoding;
}
/**
* Overrides the method from SampleResult - so the encoding can be extracted from
* the Meta content-type if necessary.
*
* Updates the dataEncoding field if the content-type is found.
*
* @return the dataEncoding value as a String
*/
@Override
public String getDataEncodingNoDefault() {
if (super.getDataEncodingNoDefault() == null && getContentType().startsWith("text/html")){ // $NON-NLS-1$
byte[] bytes=getResponseData();
// get the start of the file
String prefix = new String(bytes, 0, Math.min(bytes.length, 2000), Charset.forName(DEFAULT_HTTP_ENCODING));
// Preserve original case
String matchAgainst = prefix.toLowerCase(java.util.Locale.ENGLISH);
// Extract the content-type if present
final String METATAG = "<meta http-equiv=\"content-type\" content=\""; // $NON-NLS-1$
int tagstart=matchAgainst.indexOf(METATAG);
if (tagstart!=-1){
tagstart += METATAG.length();
int tagend = prefix.indexOf('\"', tagstart); // $NON-NLS-1$
if (tagend!=-1){
final String ct = prefix.substring(tagstart,tagend);
setEncodingAndType(ct);// Update the dataEncoding
}
}
}
return super.getDataEncodingNoDefault();
}
public void setResponseNoContent(){
setResponseCode(HTTP_NO_CONTENT_CODE);
setResponseMessage(HTTP_NO_CONTENT_MSG);
}
/* (non-Javadoc)
* @see org.apache.jmeter.samplers.SampleResult#getSearchableTokens()
*/
@Override
public List<String> getSearchableTokens() throws Exception {
List<String> list = new ArrayList<>(super.getSearchableTokens());
list.add(getQueryString());
list.add(getCookies());
list.add(getUrlAsString());
return list;
}
}