barbitoff programmer`s blog

Здесь я публикую заметки из программерской жизни: грабли, на которые мне случилось наступить, проблемы, для которых было найдено элегантное (или не очень) решение, а также все, с чем мне пришлось столкнуться и чем хотелось бы поделиться =)
PS Если хотите меня поблагодарить - на странице есть 3 места, чтобы это сделать =)
Показаны сообщения с ярлыком HTTP. Показать все сообщения
Показаны сообщения с ярлыком HTTP. Показать все сообщения

четверг, 15 сентября 2016 г.

IE: как побороть режим совместимости

Проблема

У пользователя в IE стоит галка "Display intranet sites in Compability View", из-за которой некий сайт во внутренней сетке отображается коряво. Нужно исправить ситуацию, не беспокоя конечного пользователя.

Решение

Если есть доступ к исходникам сайта, можно воспользоваться мета-тегом X-UA-Compatible, вставив его во все html-страницы:
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
Этот мета-тег превалирует над настройками совместимости браузера. Для того, чтобы тег работал, он должен идти в блоке <head> перед остальными элементами (исключение составляют <title> и другие мета-элементы). 
Другой вариант - воспользоваться аналогичным http-заголовком X-UA-Compatible, эффект будет тот же.

понедельник, 11 апреля 2016 г.

Java: подмена HTTP-заголовка, выдаваемого сервлетом / JSP-страницей

Задача

Есть некий сервлет (или JSP-страница), недоступный для модификации. Необходимо подменить один из выдаваемых сервлетом HTTP-заголоков. Например, сервлет выдает для скачивания некий файл. Необходимо подменить имя этого файла, т.е. заголовок "Content-Disposition".

Решение

Делаем jsp-страничку, которая будет служить своего рода прокси-объектом для оригинального сервлета, в ней делаем следующее:
<%
javax.servlet.http.HttpServletResponseWrapper wrappedResponse = new javax.servlet.http.HttpServletResponseWrapper(response) {
public void setHeader(String name, String value) {
if(name.equals("My-Header")) {
value = "my_value";
}
super.setHeader(name, value);
} public void addHeader(String name, String value) {
if(name.equals("My-Header")) {
value = "my_value";
}
super.addHeader(name, value);
} };
request.getRequestDispatcher("MyServlet").forward(request, wrappedResponse);
%>
Т.е. заворачиваем объект оригинального HTTP-ответа во wrapper, переопределяем методы установки заголовков, после чего выполняем forward на целевой сервлет. В итоге значение заголовка HTTP-заголовка "My-Header", генерируемого сервлетом, развернутым по относительному пути "MyServlet", будет заменено на "my_value".

среда, 5 августа 2015 г.

WSO2 ESB и IBM WebSphere MQ: "Имя свойства 'Content-Type' не является верным идентификатором Java(tm)"

Сделал простейший прокси-сервис HTTP-> JMS (в качестве JMS-провайдера используется IBM WebSphere MQ 7.5), при его вызове получаю ошибку в логах:
TID: [0] [ESB] [2015-08-05 13:10:28,219] ERROR {org.apache.axis2.transport.jms.JMSSender} -  Error creating a JMS message from the message context {org.apache.axis2.transport.jms.JMSSender}
com.ibm.msg.client.jms.DetailedMessageFormatException: JMSCC0049: Имя свойства 'Content-Type' не является верным идентификатором Java(tm).
Указанное имя свойства не соответствует разрешенному формату, описанному в спецификации JMS.
Причина в том, что с точки зрения спецификации JMS имя свойства не может содержать символов "-". WSO2 же пытается пробросить в качестве свойства JMS-сообщения пришедший во входящем вызове HTTP-заголовок. Решение:
<property action="remove" name="TRANSPORT_HEADERS" scope="axis2"/>
<property action="set" name="transport.jms.ContentTypeProperty" value="CONTENT_TYPE" scope="axis2"/>
В данном случае тип контента будет записан в JMS-свойство "CONTENT_TYPE".

пятница, 29 августа 2014 г.

Дамп HTTP-запросов tcpdump-ом

Возникла необходимость посмотреть http-заголовки запросов, приходящих на weblogic. Т.к. вывести произвольный заголовок в access-log без написания кода невозможно (http://technotoddle.blogspot.ru/2010/07/logging-more-information-in-http-logs.html), я решил особо не заморачиваться и посмотреть траффик сниффером. На Win обычно использую для подобных целей Wireshark, но здесь я был на linux-сервере без иксов, поэтому первый раз в жизни воспользовался tcpdump-ом. Делается это так:
tcpdump -A -s 0 'tcp dst port 8080 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'
(здесь сниффятся входящие запросы на порт 8080, если убрать dst, то получим в дампе и запросы, и ответы)
На выходе получил то, что хотел:
17:17:25.769127 IP xxx >yyy: . 297430808:297432068(1260) ack 1587021758 win 16695
E.....@.....
..I
..5.d....o.^...P.A7....GET /da/component/main?__dmfClientId=1409209083173&__dmfRequestId=__client1~~2&__dmfJumpType=jump&__dmfTzoff=-240 HTTP/1.1
Accept: */*
Accept-Language: en-US
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727)
Accept-Encoding: gzip, deflate
Host: 10.2.5.53:8080
Connection: Keep-Alive
Cookie: appname=da;  ...
Спасибо https://sites.google.com/site/jimmyxu101/testing/use-tcpdump-to-monitor-http-traffic. 

четверг, 6 июня 2013 г.

суббота, 23 марта 2013 г.

HTTP server stubbing

Нашел неплохой вариант для стаббинга HTTP-сервера, позволяющий автоматизировать тестирование HTTP-клиентов. Называется сабж Wiremock, имеет неплохую документацию с примерами. Есть в репозитории мавена и подключается к проекту так:
<dependency> <groupId>com.github.tomakehurst</groupId> <artifactId>wiremock</artifactId> <version>1.30</version> <scope>test</scope> </dependency>
Использовать его просто (по крайней мере, в тривиальных тестах), ниже приведен пример теста, проверяющего реакцию некоторого тестируемого класса на коды ответа HTTP-сервера 200 и 404:
import static com.github.tomakehurst.wiremock.client.WireMock.*;
...
private static final int STUB_PORT = 17777;
@Rule
public WireMockRule wireMockRule = new WireMockRule(STUB_PORT);

@Test
void someTest() {
 stubFor(get(urlEqualTo("/200"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "text/html")
.withBody("200 OK")));
// дальше некий код, запрашивающий "http://localhost:"+STUB_PORT+"/200"
stubFor(get(urlEqualTo("/404"))
.willReturn(aResponse()
.withStatus(404)
.withHeader("Content-Type", "text/html")
.withBody("404 Not Found")));
// дальше некий код, запрашивающий "http://localhost:"+STUB_PORT+"/404"
}
Проект правда неидеален, например, проверить basic-аутентификацию у меня так не получилось, метод verify почему-то игнорирует требование наличия заголовка "Authorization". Хотя я, честно говоря, особо не заморачивался с этой проблемой, возможно она решаема и без копания в исходниках Wiremock`а (которые, кстати, доступны на гитхабе: https://github.com/tomakehurst/wiremock).

вторник, 5 февраля 2013 г.

WSO2 ESB: ответ HTTP 411 Length Required от конечного сервиса

Проблема:

Некий сервис прокинут через WSO2 ESB. При вызове прокси-сервиса возвращается SOAP-Fault:
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
   <soapenv:Body>
      <soapenv:Fault>
         <soapenv:Code>
            <soapenv:Value>axis2ns4:</soapenv:Value>
         </soapenv:Code>
         <soapenv:Reason>
            <soapenv:Text xml:lang="en">Unexpected response received. HTTP response code : 411 HTTP status : Length Required exception : First Element must contain the local name, Envelope , but found html</soapenv:Text>
         </soapenv:Reason>
         <soapenv:Detail/>
      </soapenv:Fault>
   </soapenv:Body>
</soapenv:Envelope>
, из чего следует, что конечный сервис ответил странным "411 Length Required", да ещё и с html в теле ответа. При вызове же конечного сервиса из soapUI приходит корректный ответ.

Причина:

HTTP-код 411 говорит о том, что сервис требует, чтобы при его вызове был установлен заголовок "Content-Length". Шина же использует Chunked transfer encoding, не устанавливая этого заголовка. К сожалению, отключить chunked-encoding можно только переключив шину на использование HTTP 1.0 установкой свойства:
<property name="FORCE_HTTP_1.0" value="true" scope="axis2"/>
перед отправкой запроса во входящей цепочке медиации.

пятница, 18 января 2013 г.

WSO2 ESB: исключение "com.ctc.wstx.exc.WstxUnexpectedCharException: Unexpected character '-' (code 45) in prolog" при получении ответа конечного сервиса

Проблема:

Ситуация такая: некоторое сообщение шлется из прокси-сервиса А на одном экземпляре WSO2 ESB на прокси-сервис Б, развернутый на другом экземпляре WSO2. При обработке ответа  от сервиса Б сервис А валится с ошибкой:
com.ctc.wstx.exc.WstxUnexpectedCharException: Unexpected character '-' (code 45) in prolog
при этом в логах сервиса Б сообщение никаких "-" в прологе не содержит, и выглядит очень даже корректно.

Причина:

На самом деле сервис Б выдает multipart-ответ вида:
HTTP/1.1 200 OK
Content-Type: multipart/related; boundary="MIMEBoundary_68536b3ce7bdd007e0bcc56b638c8e0f9b2cd5c381cba6e9"; type="application/xop+xml"; start="<0.78536b3ce7bdd007e0bcc56b638c8e0f9b2cd5c381cba6e9@apache.org>"; start-info="text/xml"
Accept-Encoding: gzip,deflate
Host: 10.24.12.132:8280
Date: Fri, 18 Jan 2013 15:40:19 GMT
Server: Synapse-HttpComponents-NIO
Transfer-Encoding: chunked
Connection: Keep-Alive

--MIMEBoundary_68536b3ce7bdd007e0bcc56b638c8e0f9b2cd5c381cba6e9
Content-Type: application/xop+xml; charset=UTF-8; type="text/xml"
Content-Transfer-Encoding: binary
Content-ID: <0.78536b3ce7bdd007e0bcc56b638c8e0f9b2cd5c381cba6e9@apache.org>

<?xml version='1.0' encoding='UTF-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body><!--...--></soapenv:Body></soapenv:Envelope>
--MIMEBoundary_68536b3ce7bdd007e0bcc56b638c8e0f9b2cd5c381cba6e9--
Причина такого ответа в том, что на втором сервере включена MTOM-оптимизация. А вот на первом она выключена, поэтому он оптимизированных ответов не понимает.

Решение:

Т.к. для данного конкретного сервиса (особенно для его ответов), оптимизация была не нужна, я её выключил установкой свойства:
<property name="enableMTOM" value="false" scope="axis2"/>
в цепочке медиации сервиса Б перед отправкой ответа (в out / faultSequence).
Можно также отключить MTOM параметром сервиса:
<parameter name="enableMTOM">false</parameter>

четверг, 17 января 2013 г.

WSO2 ESB: исключение java.util.NoSuchElementException при отправке прокси-сервисом сообщения на конечную точку

Такое исключение, возникающее при попытке шиной отправить сообщение на конечную точку:
ERROR {org.apache.axis2.transport.base.threads.NativeWorkerPool} -  Uncaught exception {org.apache.axis2.transport.base.threads.NativeWorkerPool}
java.util.NoSuchElementException
at com.ctc.wstx.sr.BasicStreamReader.next(BasicStreamReader.java:1128)
at org.apache.axiom.util.stax.wrapper.XMLStreamReaderWrapper.next(XMLStreamReaderWrapper.java:225)
at org.apache.axiom.util.stax.dialect.DisallowDoctypeDeclStreamReaderWrapper.next(DisallowDoctypeDeclStreamReaderWrapper.java:34)
...
org.apache.axis2.transport.base.threads.NativeWorkerPool$1.run(NativeWorkerPool.java:172)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)
может быть вызвано полным отсутствием ответа сервиса (т.е. не пустым телом HTTP, а отсутствием даже заголовков). Тут как обычно с WSO2, по исключению вовсе не очевидна его причина.

пятница, 5 октября 2012 г.

php: установка заголовков запроса при загрузке данных по http с помощью функции file_get_contents()

Чтобы установить какой-нибудь заголовок http-запроса при загрузке данных по http функцией file_get_contents(), нужно использовать третий параметр этой функции - $context. Например, чтобы установить User-Agent как у 15ого Firefox`а, нужно:
$opts = array('http'=>array('header' => "User-Agent:Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20100101 Firefox/15.0.1\r\n"));
$context = stream_context_create($opts);
$html = file_get_contents($link,false,$context);
А то некоторые сайты, например, без этого плюются 500ой ошибкой и не дают себя по-человечески сграбить =)
Warning: file_get_contents(http://***.ru/***): failed to open stream: HTTP request failed! HTTP/1.1 500 Internal Server Error

вторник, 21 августа 2012 г.

Debian / Mint: apt-get за прокси с авторизацией

Чтобы apt заработал за проксей с basic http авторизацией в Debian, нужно прописать в /etc/apt/apt.conf:
Acquire{
http
{
Proxy "http://user:pass@proxy_host:proxy_port";
};
};
В Linux Mint аналогично, но из-за отсутствия apt.conf нужно создать файл в /etc/apt/apt.conf.d/, назвав его, например, "02proxy", и записать приведенный выше конфиг туда.

среда, 13 июня 2012 г.

Проброс портов c Amazon EC2-инстанса (Ubuntu 12.04) на домашний HTTP-сервер

Задача:

Есть инстанс Amazon EC2 под Ubuntu 12.04 (как любой инстанс, он имеет публичное DNS-имя), хочется пробросить HTTP-порт с него на локальный ПК, находящийся, во-первых, за NAT-ом интернет-провайдера, а, во-вторых, за домашним роутером. Внешний IP провайдер не предоставляет.

Решение:

Во-первых, я поднял OpenVPN-сервер на инстансе, чтобы объединить его с моим ПК в единую виртуальную локалку (подсеть 192.168.7.0/24, амазоновский инстанс имеет ip-адрес 192.168.7.1, мой ПК - 192.168.7.10). Конфиг сервера следующий:
; подключение
port 1194
proto udp
dev tun
; аутентификация
ca ca.crt
cert aspxvpnserver.crt
key aspxvpnserver.key
dh dh1024.pem
; адресация
server 192.168.7.0 255.255.255.0
; клиентские конфиги
client-config-dir ccd
; прочее
keepalive 10 120
comp-lzo
persist-key
persist-tun
user nobody
group nogroup
client-to-client
; логирование
status /var/log/openvpn-status.log
log-append  /var/log/openvpn.log
verb 5
Конфиг клиента:
client
; подключение
dev tun
proto udp
remote ec2-xxx-xxx-xxx-xxx.compute-1.amazonaws.com 1194
; аутентификация
ca ca.crt
cert aspxclient.crt
key aspxclient.key
; прочее
resolv-retry infinite
nobind
persist-key
persist-tun
comp-lzo
; логирование
verb 5
На сервере для клиента  aspxclient определен собственный конфиг в файле /etc/openvpn/ccd/aspxclient:
ifconfig-push 192.168.7.10 192.168.7.9
, что позволяет задать клиенту статичный IP.

Подключаю OpenVPN, поднимаю на локальном ПК HTTP-сервер, проверяю, что с сервера есть телнет на 192.168.7.10:80, и что с моего ПК идут пинги на 192.168.7.1. 

Теперь осталось настроить iptables на сервере для проброса 80 порта (конечно, ещё нужно открыть этот самый 80 порт в амазоновском файрволе):
#!/bin/sh
/sbin/modprobe iptable_nat
echo "1" > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A PREROUTING -d 10.203.50.215 -p tcp -m tcp --dport 80 -j DNAT --to-destination 192.168.7.10:80
iptables -t nat -A POSTROUTING -p tcp --dst 192.168.7.10 --dport 80 -j SNAT --to-source 192.168.7.1
exit 0
Здесь 10.203.50.215 - ip-адрес инстанса в локалке амазона. Т.к. этот адрес выдается инстансу динамически и меняется при перезапуске, удобнее вместо фильтрации по адресу назначения использовать фильтрацию по входящему интерфейсу:

#!/bin/sh
/sbin/modprobe iptable_nat
echo "1" > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A PREROUTING -i eth0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 192.168.7.10:80
iptables -t nat -A POSTROUTING -p tcp --dst 192.168.7.10 --dport 80 -j SNAT --to-source 192.168.7.1
exit 0
Ну или модифицировать скрипт так, чтобы он подхватывал ip-адрес с интерфейса eth0:
#!/bin/sh
/sbin/modprobe iptable_nat
echo "1" > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A PREROUTING -d `ifconfig eth0 | grep 'inet addr' | sed -e 's/\s*inet addr://' | sed -e 's/ .*//g'` -p tcp -m tcp --dport 80 -j DNAT --to-destination 192.168.7.10:80
iptables -t nat -A POSTROUTING -p tcp --dst 192.168.7.10 --dport 80 -j SNAT --to-source 192.168.7.1
exit 0
Для удаления созданных выше правил iptables нужно выполнить:
#!/bin/sh
iptables -t nat -D PREROUTING 1
iptables -t nat -D POSTROUTING 1
exit 0
Поместив эти 2 скрипта, соответственно, в /etc/network/if-up.d и /etc/network/if-down.d, получим  сохраняемость этих правил при перезапуске интерфейса и при перезагрузке сервера (важно не использовать точки в именах скриптов!).

вторник, 17 апреля 2012 г.

JSON POST запросы к Redmine REST API

Проблема:

При выполнении POST-запроса в JSON-формате к Redmine REST API сервер ведет себя так, как будто не воспринимает тело запроса. Например, при попытке добавить Issue возвращается ошибка 404, а в логах Redmine видно:
Processing IssuesController#create to json (for 192.168.10.156 at 2012-04-17 13:59:05) [POST]
  Parameters: {"format"=>"json", "action"=>"create", "controller"=>"issues"}
Filter chain halted as [:find_project] rendered_or_redirected.
Completed in 47ms (View: 0, DB: 10) | 404 Not Found [http://redmine-tp/issues.json]
, хотя передаваемый идентификатор проекта 100% верен (существует в таблице "projects" в БД Redmine). При этом в заголовке Content-Type запроса указывается вроде бы корректное значение "text/json"; при попытке указать неверное "text/xml" возвращается вообще 500 ошибка, а в логи валится:

/!\ FAILSAFE /!\  Tue Apr 17 14:13:38 +0400 2012
  Status: 500 Internal Server Error
  undefined method `name' for nil:NilClass
    /usr/lib/ruby/gems/1.8/gems/activesupport-2.3.14/lib/active_support/xml_mini/rexml.rb:29:in `merge_element!'
    /usr/lib/ruby/gems/1.8/gems/activesupport-2.3.14/lib/active_support/xml_mini/rexml.rb:18:in `parse'
    /usr/lib/ruby/gems/1.8/gems/activesupport-2.3.14/lib/active_support/xml_mini.rb:12:in `__send__'
...
    /usr/lib/ruby/1.8/phusion_passenger/abstract_server.rb:196:in `start_synchronously'
    /usr/lib/phusion_passenger/passenger-spawn-server:61

Причина:

Неверно установлен заголовок Content-Type запроса. Вместо "text/json" необходимо использовать "application/json".

четверг, 5 апреля 2012 г.

org.apache.commons.httpclient и библиотека Apache HttpClient 4.x

В версии Apache HttpClient 4.0 и выше используется пространство имен org.apache.http вместо org.apache.commons.httpclient. Поэтому если какие-то библиотеки ругаются на отсутствие классов в org.apache.commons.httpclient, то качать нужно не последнюю, 4ую версию Apache HttpClient, а старую третью. Например, отсюда: http://archive.apache.org/dist/httpcomponents/commons-httpclient/.

среда, 28 марта 2012 г.

Конфигурация Apache2 HTTP Server: редирект с HTTPS на HTTP

Выполняется средствами mod_rewrite:
RewriteEngine On
RewriteCond %{HTTPS} on
RewriteRule (.*) http://%{HTTP_HOST}%{REQUEST_URI}
При этом, однако, корректная настройка SSL все равно необходима, т.к. редирект осуществляется после установления защищенного соединения.
Можно также явно указать, каким кодом отвечать при редиректе. Например, для отправки кода 301 ("Перемещено окончательно") нужно:
RewriteEngine On
RewriteCond %{HTTPS} on
RewriteRule (.*) http://%{HTTP_HOST}%{REQUEST_URI} [R=permanent]
Если некоторые URL`ы требуется всё же обрабатывать по HTTPS, нужно исключить их из правила:
RewriteEngine On
RewriteCond %{HTTPS} on
RewriteCond $0 !/needhttps/
RewriteRule (.*) http://%{HTTP_HOST}%{REQUEST_URI} 

среда, 29 февраля 2012 г.

Модификация Path в заголовке Set-Cookie, отправляемом backend-сервером, сидящим за Apache2 с mod_proxy

Проблема:

За Apache2 веб-сервером сидит backend-сервер Tomcat, при этом путь на Apache2, по которому ведется проксирование, отличается от пути на Tomcat`е, и, следовательно, куки, устанавливаемые томкатом, оказываются недействительными, т.к. в заголовке Set-Cookie браузер видит не тот Path, который указан в URL`е.

Решение:

mod_proxy предусматривает модификацию отправляемого клиенту заголовка Set-Cookie директивой ProxyPassReverseCookiePath. Например, если запросы в корень апача проксируются на путь /mypath томката, директива будет выглядеть следующим образом:
ProxyPassReverseCookiePath /mypath /
Тогда заголовки вида:
Set-Cookie JSESSIONID=2CAF42328709F0A93ABD515651002089; Path=/mypath
будут преобразовываться проксёй в:
Set-Cookie JSESSIONID=2CAF42328709F0A93ABD515651002089; Path=/

среда, 18 января 2012 г.

Просмотр HTTP-заголовков в Firefox

Смотреть HTTP-заголовки запросов/ответов позволяет дополнение HttpFox. Помимо заголовков, можно смотреть куки, данные POST и содержимое ответа сервера.

вторник, 17 января 2012 г.

IE, загрузка файлов и Cache-Control: no-cache, no-store

Проблема:
Заголовок "Cache-Control: no-cache, no-store", предусмотренный в HTTP/1.1 для предотвращения кэширования информации браузерами и прокси-серверами, будучи установленным для ответа, отсылающего файл, вызывает у Internet Explorer появление сообщения (правда не в 100% случаев, я так и не нашел закономерности):
"Не удалось открыть этот интернет-узел. Запрошенный узел недоступен или не найден..."
и файл, естественно, не загружается.

Решение:
Ничего не поделаешь, для файлов заголовок "Cache-Control: no-cache, no-store" придется не ставить. Впрочем, я думаю IE и так не будет кэшировать файлы =)

понедельник, 16 января 2012 г.

HttpServlet: установка Content-Disposition заголовка для скачивания файла с русскими символами в имени

Проблема:
Задача следующая: отправить в браузер файл с русскими символами в имени. Проблема заключается в том, что если помещать имя файла в заголовок непосредственно кодом:
response.setHeader("Content-Disposition", "attachment; filename="+fileName);
вместо корректного имени получаем кракозябры, а IE 8 вообще выкидывает невнятное сообщение о невозможности загрузки.

Решение:
Ниже приведет кроссбраузерный код (работает в IE 8, FF 9, Opera 11.60, Chrome 16):

        if(request.getHeader("user-agent")!=null && request.getHeader("user-agent").indexOf("Firefox")!=-1) // Firefox
            response.setHeader("Content-Disposition", "attachment; filename*=utf-8''"+java.net.URLEncoder.encode(fileName, "UTF-8") + ";");
        else
            response.setHeader("Content-Disposition", "attachment; filename="+java.net.URLEncoder.encode(fileName, "UTF-8"));