How do I parameterize the Content-Disposition filename when a JMeter uploads a file ?, Contentdisposition

Source: Internet
Author: User

How do I parameterize the Content-Disposition filename when a JMeter uploads a file ?, Contentdisposition
When the Problem description file is uploaded, the user-defined Content-Disposition is invalid.
When I write a stress test script, I have an upload page. The server defines the file name of the file to be saved Based on the filename value in the Content-Disposition. However, testers cannot prepare a different file for each request (this workload is too heavy ), therefore, the filename in the Content-Disposition that JMeter sends to the server must be random but not repeated.
Someone asked, is the file name uploaded by the browser to the server also the file name? No. The filename modified in js is as follows:

Uploader. onBeforeUploadItem = function (item) {// modify the name var timeStamp = new Date (). getTime (); var fileName = item. file. name; item. file. name = timeStamp + fileName. substr (fileName. lastIndexOf ('. '); var day = $ filter ('date') (new date (), 'yyyymmdd'); item. url = [item. url, "batchImport", item. importType, day, session. userId]. join ("/");};

I tried multiple methods to modify the filename value received by the server, and the results failed. The methods I have tried are:
1. Add HTTP parameters
, We expect the server to receive the filename value of 4000.xls, rather than 255.255..xls.
The result server receives the *.xls. The storage path returned by the server to the client is:/batchImport/merAdd/20141128/1/paipai.xls. To view this HTTP request, you can see the following information:
POST http: // serverIP/upload/batchImport/merAdd/20141128/1

POST data:
-- DoZtX5jrOIxJTocysPzYJ1WVqtoagXMQHHqho4i
Content-Disposition: form-data; name = "Content-Disposition"
Content-Type: text/plain; charset = US-ASCII
Content-Transfer-Encoding: 8bit

Form-data; name = "14170058206940.xls"; filename =" 20174000.xls"
-- DoZtX5jrOIxJTocysPzYJ1WVqtoagXMQHHqho4i
Content-Disposition: form-data; name = "file"; filename =" 00000000. xls"
Content-Type: application/vnd. ms-excel
Content-Transfer-Encoding: binary

<Actual file content, not shown here>
-- DoZtX5jrOIxJTocysPzYJ1WVqtoagXMQHHqho4i --

Cookie Data:
$ Version = 0; JSESSIONID = AC79777AEFE5AFC690623FCCB09E5DD5; $ Path =/

Request Headers:
Connection: keep-alive
Content-Length: 34786
Content-Type: multipart/form-data; boundary = DoZtX5jrOIxJTocysPzYJ1WVqtoagXMQHHqho4i
Host: serverIP
User-Agent: Apache-HttpClient/4.2.6 (java 1.5)
It seems that JMeter has lost our Content-Disposition parameter names.
2. Add the HTTP information header Manager
, We expect the server to receive the filename value 40004000.xls, instead of 255.255..xls.
Then we send a request and view the HTTP request. The following information is displayed:
POST http: // serverIP/upload/batchImport/merAdd/20141128/1

POST data:
-- BNKvCNweqwpTJToYINcDn6JJfzjazBE550a-
Content-Disposition: form-data; name = "file"; filename =" 00000000. xls"
Content-Type: application/vnd. ms-excel
Content-Transfer-Encoding: binary

<Actual file content, not shown here>
-- Bnkvcnweqwptjtoyincdn6jfzjazbe550a ---

Cookie Data:
$ Version = 0; JSESSIONID = 49AB53310FB7241B5544B4E747A58F80; $ Path =/

Request Headers:
Connection: keep-alive
Content-Disposition: form-data; name = "file"; filename = "40004000.xls"
Content-Length: 34535
Content-Type: multipart/form-data; boundary = BNKvCNweqwpTJToYINcDn6JJfzjazBE550a-
Host: serverIP
User-Agent: Apache-HttpClient/4.2.6 (java 1.5)
This time, JMeter did not lose our Content-Disposition. It appears in the Request Headers. However, the server seems to be reading the filename in Content-Disposition in POST data. The storage path returned by the server to the client is:/batchImport/merAdd/20141128/1/ 00000000. xls.
3. Use BeanShell
, We expect the server to receive the filename value 40004004.xls, instead of 255.255..xls. We initiate another request to the server with expectation. The request is as follows:
POST http: // serverIP/upload/batchImport/merAdd/20141128/1

POST data:
-- LWS2eUVPPPuDxcfT7dS4RpqQJe2uP_0lAme6Qx2Q
Content-Disposition: form-data; name = "file"; filename =" 00000000. xls"
Content-Type: application/vnd. ms-excel
Content-Transfer-Encoding: binary

<Actual file content, not shown here>
-- LWS2eUVPPPuDxcfT7dS4RpqQJe2uP_0lAme6Qx2Q --

Cookie Data:
$ Version = 0; JSESSIONID = 81514F48024CE0B4CB53DB0CBC283C11; $ Path =/

Request Headers:
Connection: keep-alive
Content-Length: 34543
Content-Type: multipart/form-data; boundary = LWS2eUVPPPuDxcfT7dS4RpqQJe2uP_0lAme6Qx2Q
Host: serverIP
User-Agent: Apache-HttpClient/4.2.6 (java 1.5)
The result is that no matter whether it is the front BeanShell or BeanShell listener, we obviously have no choice about our needs.
For help from omnipotent Google, the result is basically It's impossible.
Finally, I went to the project owner with a depressing mood and tried to persuade him that the server should not name the stored file with the filename sent from the client, you should have a set of rules for randomly generating file names. The answer is: NO.
In desperation, I had to look at the JMeter source code. Well, the source code of JMeter 2.12 (the pure *. java file under the src directory) is 6.75 MB. In addition, Ant code is used for management, and senren is under pressure. Well, you can't help it. Who asked me to take the performance test bowl? I always want to continue my work.
The result was unfortunate when I tried to write the filename in Content-Disposition to JMeter. The root of JMeter was that it was not intended for users to parameterize the filename!
For example, the writeStartFileMultipart method of org. apache. jmeter. protocol. http. sampler. PostWriter is written as follows:
    /**     * Write the start of a file multipart, up to the point where the     * actual file content should be written     */    private void writeStartFileMultipart(OutputStream out, String filename,            String nameField, String mimetype)            throws IOException {        write(out, "Content-Disposition: form-data; name=\""); // $NON-NLS-1$        write(out, nameField);        write(out, "\"; filename=\"");// $NON-NLS-1$        write(out, new File(filename).getName());        writeln(out, "\""); // $NON-NLS-1$        writeln(out, "Content-Type: " + mimetype); // $NON-NLS-1$        writeln(out, "Content-Transfer-Encoding: binary"); // $NON-NLS-1$        out.write(CRLF);    }

Although angry, I feel that I have not come to the wrong place and continue to read the source code.
View the unit test code org. apache. jmeter. protocol. http. sampler. PostWriterTest of PostWriter. In the sendPostData method, run the following statement:
PostWriter. setHeaders (connection, sampler );
PostWriter. sendPostData (connection, sampler );
That is to say, first write the header and then write the post package. The sample method of org. apache. jmeter. protocol. http. sampler. HTTPJavaImpl also confirms this:
                try {                    conn = setupConnection(url, method, res);                    // Attempt the connection:                    savedConn = conn;                    conn.connect();                    break;                } catch (BindException e) {                    if (retry >= MAX_CONN_RETRIES) {                        log.error("Can't connect after "+retry+" retries, "+e);                        throw e;                    }                    log.debug("Bind exception, try again");                    if (conn!=null) {                        savedConn = null; // we don't want interrupt to try disconnection again                        conn.disconnect();                    }                    setUseKeepAlive(false);                    continue; // try again                } catch (IOException e) {                    log.debug("Connection failed, giving up");                    throw e;                }            }            if (retry > MAX_CONN_RETRIES) {                // This should never happen, but...                throw new BindException();            }            // Nice, we've got a connection. Finish sending the request:            if (method.equals(HTTPConstants.POST)) {                String postBody = sendPostData(conn);                res.setQueryString(postBody);            }

Conn = setupConnection (url, method, res); when the connection is established, the header is written (in the setupConnection method below), followed by String postBody = sendPostData (conn ); before sending a file.
Why does JMeter ignore HTTP header settings?
Let's take a look at the setupConnection method of org. apache. jmeter. protocol. http. sampler. HTTPJavaImpl:
    protected HttpURLConnection setupConnection(URL u, String method, HTTPSampleResult res) throws IOException {        SSLManager sslmgr = null;        if (HTTPConstants.PROTOCOL_HTTPS.equalsIgnoreCase(u.getProtocol())) {            try {                sslmgr=SSLManager.getInstance(); // N.B. this needs to be done before opening the connection            } catch (Exception e) {                log.warn("Problem creating the SSLManager: ", e);            }        }        final HttpURLConnection conn;        final String proxyHost = getProxyHost();        final int proxyPort = getProxyPortInt();        if (proxyHost.length() > 0 && proxyPort > 0){            Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));            //TODO - how to define proxy authentication for a single connection?            // It's not clear if this is possible//            String user = getProxyUser();//            if (user.length() > 0){//                Authenticator auth = new ProxyAuthenticator(user, getProxyPass());//            }            conn = (HttpURLConnection) u.openConnection(proxy);        } else {            conn = (HttpURLConnection) u.openConnection();        }        // Update follow redirects setting just for this connection        conn.setInstanceFollowRedirects(getAutoRedirects());        int cto = getConnectTimeout();        if (cto > 0){            conn.setConnectTimeout(cto);        }        int rto = getResponseTimeout();        if (rto > 0){            conn.setReadTimeout(rto);        }        if (HTTPConstants.PROTOCOL_HTTPS.equalsIgnoreCase(u.getProtocol())) {            try {                if (null != sslmgr){                    sslmgr.setContext(conn); // N.B. must be done after opening connection                }            } catch (Exception e) {                log.warn("Problem setting the SSLManager for the connection: ", e);            }        }        // a well-bahaved browser is supposed to send 'Connection: close'        // with the last request to an HTTP server. Instead, most browsers        // leave it to the server to close the connection after their        // timeout period. Leave it to the JMeter user to decide.        if (getUseKeepAlive()) {            conn.setRequestProperty(HTTPConstants.HEADER_CONNECTION, HTTPConstants.KEEP_ALIVE);        } else {            conn.setRequestProperty(HTTPConstants.HEADER_CONNECTION, HTTPConstants.CONNECTION_CLOSE);        }        conn.setRequestMethod(method);        setConnectionHeaders(conn, u, getHeaderManager(), getCacheManager());        String cookies = setConnectionCookie(conn, u, getCookieManager());        setConnectionAuthorization(conn, u, getAuthManager());        if (method.equals(HTTPConstants.POST)) {            setPostHeaders(conn);        } else if (method.equals(HTTPConstants.PUT)) {            setPutHeaders(conn);        }        if (res != null) {            res.setRequestHeaders(getConnectionHeaders(conn));            res.setCookies(cookies);        }        return conn;    }

In this method, an http connection is established. before returning the connection, write the content in the user HTTP information header manager into the connection (setConnectionHeaders (conn, u, getHeaderManager (), getCacheManager (); sentence), and then call the PostWriter write header method (that is, setPostHeaders (conn); sentence ). This also explains the two Content-Disposition problems mentioned in this article.
That is to say, the Content-Disposition Header is written twice! Unfortunately, HTTP does not perform repetitive verification on Content-Disposition! Unfortunately, even if HTTP performs Content-Disposition repeatability verification, the custom Content in our header information manager will not work. The above Code has already stated that, JMeter first writes the attributes in the header information manager, and then calls PostWriter to write Content-Disposition. The latter overwrites the former!
This is terrible. This should be a bug in JMeter, or something that is not good enough, because it blocks our custom Content-Disposition path.
Solution HTTP request-Implementation: select the Java solution. Since JMeter blocks this path, we can open this path-simply adjust the writeStartFileMultipart of the source code of PostWriter:
    /**     * Write the start of a file multipart, up to the point where the     * actual file content should be written     */    private void writeStartFileMultipart(OutputStream out, String filename,            String nameField, String mimetype)            throws IOException {        write(out, "Content-Disposition: form-data; name=\""); // $NON-NLS-1$        write(out, nameField);        write(out, "\"; filename=\"");// $NON-NLS-1$        write(out, nameField);        writeln(out, "\""); // $NON-NLS-1$        writeln(out, "Content-Type: " + mimetype); // $NON-NLS-1$        writeln(out, "Content-Transfer-Encoding: binary"); // $NON-NLS-1$        out.write(CRLF);    }

In fact, I only changed the original write (out, new File (filename). getName (); To write (out, nameField );
Decompress ApacheJMeter_http.jar In the lib/ext directory under the JMeter installation directory, and modify the compiled PostWriter. class, decompress the original package (jar-cvf ApacheJMeter_http.jar *), delete the original ApacheJMeter_http.jar, and use the new package.
The preceding figure shows the default HTTP request solution for the sample machine JVM (that is, your sample machine-HTTP request-Implementation selects Java ). What if we select HttpClient4 or HttpClient3.1 for the HTTP request? (If you do not select HttpClient4, JMeter defaults to HttpClient4. I use the default one, that is, Implementation is not selected )? The following are the solutions for HttpClient4.

HTTP request-Implementation: HttpClient4 solution. Check the sendPostData method of org. apache. jmeter. protocol. http. sampler. HTTPHC4Impl,
Find the following sentence:
            ViewableFileBody[] fileBodies = new ViewableFileBody[files.length];            for (int i=0; i < files.length; i++) {                HTTPFileArg file = files[i];                fileBodies[i] = new ViewableFileBody(new File(file.getPath()), file.getMimeType());                multiPart.addPart(file.getParamName(),fileBodies[i]);            }            post.setEntity(multiPart);            if (multiPart.isRepeatable()){                ByteArrayOutputStream bos = new ByteArrayOutputStream();                for(ViewableFileBody fileBody : fileBodies){                    fileBody.hideFileData = true;                }                multiPart.writeTo(bos);                for(ViewableFileBody fileBody : fileBodies){                    fileBody.hideFileData = false;                }                bos.flush();                // We get the posted bytes using the encoding used to create it                postedBody.append(new String(bos.toByteArray(),                        contentEncoding == null ? "US-ASCII" // $NON-NLS-1$ this is the default used by HttpClient                        : contentEncoding));                bos.close();

We can see that the file information is in postedBody. append (new String (bos. toByteArray (), the sentence is written to the post body (if you are interested, you can track the breakpoint or perform log verification). It writes the ViewableFileBody object. Find the ViewableFileBody class. The source code is:
    // Helper class so we can generate request data without dumping entire file contents    private static class ViewableFileBody extends FileBody {        private boolean hideFileData;                public ViewableFileBody(File file, String mimeType) {            super(file, mimeType);            hideFileData = false;        }        @Override        public void writeTo(final OutputStream out) throws IOException {            if (hideFileData) {                out.write("<actual file content, not shown here>".getBytes());// encoding does not really matter here            } else {                super.writeTo(out);            }        }    }

It can be seen that it inherits from org. apache. http. entity. mime. content. FileBody. The FileBody has the getFilename method. view its source code:
    public String getFilename() {        return this.file.getName();    }

Now, let's start from here. Modify the internal ViewableFileBody class of org. apache. jmeter. protocol. http. sampler. HTTPHC4Impl as follows:
    // Helper class so we can generate request data without dumping entire file contents    private static class ViewableFileBody extends FileBody {        private boolean hideFileData;                public ViewableFileBody(File file, String mimeType) {            super(file, mimeType);            hideFileData = false;        }        @Override        public void writeTo(final OutputStream out) throws IOException {            if (hideFileData) {                out.write("<actual file content, not shown here>".getBytes());// encoding does not really matter here            } else {                super.writeTo(out);            }        }                @Override        public String  getFilename() {        String filename = this.getFile().getName();        filename = System.currentTimeMillis() + filename.substring(filename.lastIndexOf('.'));        return filename;                }    }

OK, compile-overwrite-package, and delete the original ApacheJMeter_http.jar under lib/ext under the JMeter installation directory, and use the new package. Run the test again. The intercepted HTTP request is as follows:
POST http: // serverIP/upload/batchImport/merAdd/20141128/1

POST data:
QpmvliwpJdOaJSQGKd-Ux3tR_7HnPX3s1K8KA
Content-Disposition: form-data; name = "file"; filename =" 1417182984171. xls"
Content-Type: application/vnd. ms-excel
Content-Transfer-Encoding: binary

<Actual file content, not shown here>
-- QpmvliwpJdOaJSQGKd-Ux3tR_7HnPX3s1K8KA --

Cookie Data:
$ Version = 0; JSESSIONID = 56E8E454EA4F1378AAE45DD0A89A9FE5; $ Path =/

Request Headers:
Connection: keep-alive
Content-Length: 34542
Content-Type: multipart/form-data; boundary = QpmvliwpJdOaJSQGKd-Ux3tR_7HnPX3s1K8KA
Host: serverIP
User-Agent: Apache-HttpClient/4.2.6 (java 1.5)
As you can see, filename is no longer in zookeeper .xls, and the storage path returned by the server is/batchImport/merAdd/20141128/1/ 1417182984171. xls. Succeeded.
Note: The JMeter Binaries and Source versions downloaded by the author are 2.12.
The HTTP request-Implementation selects the HttpClient3.1 solution. Well, that is, org. apache. jmeter. protocol. http. sampler. HTTPHC3Impl. This is left for smart readers to implement :)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.