HTTP протокол и работа с Web в Java. Протокол HTTP
( Константин Андрухин )
- HTTP запросы. CGI интерфейс. Методы
- HTTP запрос
- Параметры HTTP запроса
- Отправка файла методом POST
- HTTP ответ
- Коды ответов сервера
- Параметры HTTP ответа
Протокол HTTP работает поверх TCP/IP. Фактически же это означает, что клиент открывает сокет до сервера, пишет туда HTTP запрос (request), сервер читает запрос, обрабатывает его и посылает результат обработки (response) обратно клиенту.
Любой HTTP запрос, как и любой ответ по этому протоколу состоит из двух блоков: заголовок и собственно данные. Заголовок отделён от данных двойным символом переноса строки (в Java это будет "\n\n", хотя допускается и "\r\n\r\n" для платформы Windows).
Так как HTTP был изначально ориентирован на пересылку прежде всего текстовой информации, то HTTP заголовок является полностью текстовым, все символы, передающиеся в нём, являются печатными (прежде всего цифры и литеры латинского алфавита A-Z, a-z, а также набор других отображаемых символов + символ переноса строки "\n" или "\r\n"). При передаче в HTTP заголовке других символов, будет выдана ошибка "400 Вad request".
HTTP запросы. CGI интерфейс. Методы
Разберём подробнее HTTP запрос клиента. Он может выглядеть например так:
POST http://localhost/ HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */* Accept-Language: ru User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705) Host: localhost Proxy-Connection: Keep-Alive param1=1¶m2=2
Взглянув на пример, можно заметить, что запрос начинается со слова "POST". Это слово означает метод передачи данных на сервер, в котором дополнительные данные запроса (строка "param1=1¶m2=2") передаются после заголовка.
В HTML документах, метод передачи данных указывается в форме отправки сообщений. Например для того, чтобы получить этот запрос, была использована следующая форма:
<form action="http://localhost/" method="post"> <input type=hidden name="param1" value="1"> <input type=hidden name="param2" value="2"> <input type=submit></form>
Как видно из примера, параметры записываются в виде
[имя параметра 1]=[значения параметра 1]& [имя параметра 2]=[значения параметра 2]&...
Такой вид записи является стандартным и носит название CGI интерфейса (Common Gateway Interface — базовый интерфейс гейтов (gate — врата, ещё одно название серверов)). Все данные, отсылаемые браузером, обработавшим HTML к серверу записываются именно в таком формате. При этом символы, отличные от печатных ANSI, записываются в формате %NN, где NN — это шестнадцатиричный код символа. К примеру, пробел будет записан как %20, а символ % — как %25 (см. ASCII & ANSI Character Codes). Так как русские кириллические символы не входят в набор печатных ANSI символов, то в HTTP заголовках они тоже заменяются подобным образом.
Наиболее часто употребим ещё один метод запроса — "GET". Фактически все запросы, не требующие отправки данных — например запрос страницы, производятся этим способом. Впрочем, данные можно отправлять и GET методом — изменим форму запроса:
<form action="http://localhost/" method="get"> <input type=hidden name="param1" value="1"> <input type=hidden name="param2" value="2"> <input type=submit></form>
и получим следующий HTTP запрос:
GET http://localhost/?param1=1¶m2=2 HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */* Accept-Language: ru User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705) Host: localhost Proxy-Connection: Keep-Alive
Как видно, строка "param1=1¶m2=2" переместилась выше и добавилась к строке "http://localhost/" после знака "?". Так же изменилось первое слово в HTTP заголовке, остальное осталось без изменения.
Достоинством метода GET является то, что в строке браузера видно, какие данные были отправлены. К недостаткам же относится то, что длина отправляемых данных таким способом (в отличие от метода POST) ограничена — некоторые серверы, как и некоторые браузеры, имеют лимит на длину адреса запрашиваемого документа. Соответственно адрес с длинной строкой запроса может быть либо обрезан, либо сервер возвратит ошибку "414 Request-URI Too Long".
HTTP запрос
Разберём по строкам HTTP заголовок запроса:
Первая строка, первое слово — имя метода запроса. Это слово может быть одно из следующих:
OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT
В данной статье я буду касаться прежде всего двух методов — GET и POST, подробнее об остальных читайте тут — RFC 2616, Section 5. Вкратце лишь опишу действия остальных:
Метод HEAD по действию практически идентичен методу GET с одним отличием — в ответе на метод HEAD сервер выдаёт только HTTP заголовок, не выдавая содержимого документа.
Метод PUT по действию идентичен методу POST, но как и HEAD выдаёт только заголовок HTTP.
Метод OPTIONS выдаёт все действия, которые можно совершить с документом.
Метод DELETE указывает серверу, чтобы он предпринял попытку к удалению документа. Возможным ответом является ошибка политики безопасности ("403 Forbidden").
Методом TRACE можно получить путь запроса до сервера, список узловых точек, гейтов (Gate), путь через прокси-сервера.
Метод CONNECT возвращает есть ли связь с сервером и поддерживает ли сервер HTTP протокол.
Сразу после ключевого слова, определяющего метод, идёт символ пробела и указан URI документа запрашиваемого с сервера. После URI документа идёт ещё один символ пробела и название протокола (строка "HTTP/1.1").
Что такое URI? URI расшифровывается как Uniform Resource Identifier (формат записи индитефикатора ресурса), полностью он описан тут — RFC 2396, а нас интересует лишь то, как с помощью его записывается адрес документа. Для HTTP существует разновидность стандарта URI, называемая URL (Uniform Resource Location — формат записи нахождения ресурса), к примеру
http://devresource.org:80/javalinks/catalog.php3?name=java&cat=2#section1
Из приведённого примера URL можно выделить логические части:
1) [http://] 2) [devresource.org:80/javalinks/catalog.php3] 3) [?name=java&cat=2] 4) [#section1]
часть (1) указывает на протокол доступа к документу, часть (2) — можно разбить на две части —
[devresource.org] [:80] [/javalinks/catalog.php3]
это имя хоста (вместо имени devresource.org может стоять и IP адрес), порт сервера через символ ":" и путь (path) до документа от корня (root) сервера. Стандартным портом для HTTP сервера является порт 80 и, поэтому, его можно не указывать.
Третья часть URL — это GET часть запроса, отделена от документа символом "?".
И, наконец, последняя часть URI — "секция", отделённая символом "#". При HTML форматировании в документа можно положить закладку (по другому - установить якорь, он же anchor, отсюда и название HTML тега <A>). Если в URI ресурса указана секция, то в HTML ищется одноимённая закладка, а браузер при отображении документа показывает текст, отмеченный якорем.
К примеру, для HTML документа sample.html
... test 24 <a name="section1">test 25</a> test 26 <a name="section2">test 27</a> test 28 ...
при вызове sample.html#section1 документ будет проскролирован до закладки "section1", а при указании sample.html#section2 — будет показано место, помеченное в документе, как "section2".
Части URL с номерами (3) и (4) являются необязательными. Если нет необходимости, их можно не указывать. В первой строке HTTP запроса так же можно указать не полный URL, а лишь путь до документа — к примеру так:
GET /#section1 HTTP/1.1
или так:
GET /javalinks/catalog.php3?name=java&cat=2#section1 HTTP/1.1
В таких случаях имя хоста берётся из параметра HTTP запроса "Host".
GET localhost/?param1=1¶m2=2 HTTP/1.1
то сервер будет искать документ
http://localhost/localhost/?param1=1¶m2=2
а не
http://localhost/?param1=1¶m2=2
Как и каждая строка HTTP заголовка, первая строка запроса заканчивается символом переноса строки ("\n").
Параметры HTTP запроса
Далее приведены основные параметры для HTTP заголовка. Каждая строка, содержащая параметр начинается с ключевого слова (например "Host"), потом идёт символ двоеточие, пробел, значение параметра и символ переноса строки. Приведённые параметры соответствуют стандарту RFC 2616 (HTTP/1.1). Здесь приводится не весь список возможных полей запроса и их значений.
Host: localhost
Этот параметр содержит имя хоста, например "localhost" или "localhost:80" (если порт 80, то его можно не указывать, если порт отличается от 80, то его нужно обязательно указать). Это второй обязательный параметр HTTP заголовка (первый — HTTP метод и имя протокола).
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*
В этом параметре через запятую указаны MIME типы документов, которые способен обработать браузер. Так же в MIME типе указываются доступные к обработке кодировки документов. Подробнее о стандарте MIME смотрите тут — RFC 2045. Символ "*" в указании типа означает, что браузер может обработать весь класс документов. К примеру, image/* означает, что браузер может обработать и image/gif, и image/x-xbitmap, и image/jpeg, и image/pjpeg и вообще любые документы изображений, а заключительный тип — */* указывает, что браузер обработает любые документы, присланные сервером. На деле это обычно означает, что если MIME тип присланного сервером документа браузеру неизвестен, то он предложит сохранить его на диск.
Accept-Language: ru, en Accept-Charset: windows-1251, KOI8-R
Эти параметры отвечают за языки. В первом — через запятую указываются предпочтительные языки для сервера. В частности Google.com, обработав этот параметр, перенаправит вас на русскоязычную страничку. Во втором — кодировка, в которой закодированы символы в CGI запросе. Также через запятую могут быть указаны предпочитаемые кодировки для ответа сервера.
Accept-Encoding: compressed, gzip
Тут указаны возможные варианты пересылки данных. В частности, я привёл в пример запроса, показывающего, что браузер готов принимать HTML документ в сжатом виде.
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; MyIE2; .NET CLR 1.0.3705)
Имя HTTP клиента. Многие браузеры тут же указывают операционную систему, плагины и прочие нашлёпки.
Referer: http://localhost/?test=test
Очень полезный параметр. Значением этого поля является URL ресурса, с которой был осуществлён переход. Фактически, когда вы нажимаете на ссылку в HTML документе, скорее всего адрес этого документа будет записан в этот параметр.
Cookie: param1=value1; param2=value2
В этом параметре браузер отправляет cookie (или просто куки) — данные, записанные сервером на компьютер клиента. Как видно, куки отправляются не с помощью CGI интерфейса, их форматирование отличается:
[имя параметра1]=[значение параметра1]; [имя параметра2]=[значение параметра2]; ...
Впрочем, значения параметров кодируются точно так же, как и в CGI - "неправильные" символы заменяются с помощью %NN. Подробнее о куках читайте тут — Cookie Specification.
Range-Unit: 2015 | 1024
Очень полезный параметр, позволяющий получить с сервера не весь документ, а только его часть. Именно этот параметр используют менеджеры докачек типа flashget. В данном примере указано, что клиент хочет получить кусок документа, начиная с 2015 байта и длиной в 1 килобайт. Если сервер поддерживает докачку и документ не является динамическим, то будет выдана запрашиваемая часть. В противном случае сервер вернёт ошибку о том, что действие не поддерживается или начнёт выдавать документ полностью.
Pragma: no-cache Cache-Control: no-cache, must-revalidate
Параметры, указывающие серверу, что этот документ не надо брать из кэша. Другие варианты значений могут быть:
- "public" — документ является публичным, его может брать любой клиент из кэша
- "private" — документ является приватным, только для данного клиента
- "no-store" — не сохранять в кэш
- "no-transform" — не модифицировать документ, уже содержащийся в кэше
- "must-revalidate" — обязан обновить документ, лежащий в кэше (и браузер и прокси)
- "proxy-revalidate" — в кэше должен обновить только прокси сервер
- "max-age=[seconds]" — сохраняет в кэш на количество секунд, указанных в параметре, начиная со времени сохранения; по истечению этого времени, документ удаляется
Допустимо указание параметра в следующем виде:
Pragma: must-revalidate, max-age=1000
но некоторые комбинации значений могут вызвать ошибку "400 Bad request"
Proxy-Connection: Keep-Alive
Параметр указывает на то, что соединение с сервером будет поддерживаться постоянно. Другой вариант -
Proxy-Connection: close
означает, что браузер уже послал все данные серверу и теперь будет только ждать ответа.
Отправка файла методом POST
Отдельно стоит рассказать об отправке файлов с помощью метода POST. Для того, чтобы отправить файл этим методом, нужно в форме отправки сообщений указать специальный параметр "enctype='multipart/form-data'":
<form action='http://localhost' method=post enctype='multipart/form-data'> <input type='hidden' name='test' value='test'> <input type='file' name='testfile'> <input type='submit' value='send'></form>
Допустим, мы отправляем файл "c:\test.txt" размера 14 байт, содержащий текст "This a test!!!". В этом случае данные будут отправлены следующим образом:
POST http://localhost/ HTTP/1.1 Content-Type: multipart/form-data; boundary=---------------------------7d33188e01e4 Host: localhost Content-Length: 254 -----------------------------7d33188e01e4 Content-Disposition: form-data; name="test" test -----------------------------7d33188e01e4 Content-Disposition: form-data; name="testfile"; filename="c:\test.txt" Content-Type: text/plain This a test!!!
Как видно из примера, добавляется ещё два HTTP параметра -
Content-Type: multipart/form-data; boundary=---------------------------7d33188e01e4
Эта строка говорит, что все отсылаемые данные будут передаваться по частям, а делителем этих частей будет выступать строка "-----------------------------7d33188e01e4" и перенос строки после неё. Вообще-то, делителем может выступать совершенно любой набор символов, лишь бы подобного не было в передаваемых данных.
Content-Length: 342
Этот параметр сообщает серверу количество данных, содержащихся после HTTP заголовка.
Ну и сами данные передаются с помощью HTTP-subheader (субзаголовок HTTP)
Content-Disposition: form-data; name="test"
Тут указывается название переменной, после чего идут два символа переноса строки и сами данные. Конец данных означает либо символ переноса строки и делитель (boundary), либо конец полученых данных.
Другой субзаголовок —
Content-Type: text/plain
Он передаёт серверу MIME тип отправляемого файла. Нужно заметить, что при передаче данных таким способом, непечатные символы не заменяются на %NN, а отправляются как есть.
HTTP ответ
Перейдём к ответу сервера. Вот пример ответа сервера клиенту (сервер выдаёт текстовый файл, содержащий строку "This a test!!!"):
HTTP/1.1 200 OK Date: Mon, 07 Apr 2003 14:40:25 GMT Server: Apache/1.3.20 (Win32) PHP/4.3.0 Keep-Alive: timeout=15, max=100 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: text/plane This a test!!!
Ещё один пример, сервер выдаёт файл test.zip:
HTTP/1.1 200 OK Date: Mon, 07 Apr 2003 14:51:19 GMT Server: Apache/1.3.20 (Win32) PHP/4.3.0 Last-Modified: Mon, 07 Apr 2003 14:51:00 GMT Accept-Ranges: bytes Content-Length: 673 Keep-Alive: timeout=15, max=100 Connection: Keep-Alive Content-Type: application/zip Content-Disposition: attachment; filename=test.zip Pragma: no-cache ....(содержимое zip файла)
Тут мы тоже видим HTTP заголовок, отделённый от тела документа двумя символами переноса строки.
Разберём заголовок. Он начинается с названия протокола "HTTP/1.1", после чего идёт пробел, затем — код возврата "200 OK". После кода возврата идёт символ переноса строки.
Коды ответов сервера
Вот основные коды возврата, определённые для серверов:
Коды с номером типа 1xx: информационный — запрос послан, идёт процесс:
- "101 Switching Protocols" — переключение протокола
Коды с номером типа 2xx: удачное завершение — запрос полностью послан, прочитан/понят сервером и принят им:
- "200 OK" — запрос успешно получен, понят, принят и выполнен
- "201 Created" — создано
- "202 Accepted" — принято
- "203 Non-Authoritative Information" — нерабочая информация
- "204 No Content" — нет информации к ответу
- "205 Reset Content" — очистка ответа
- "206 Partial Content" — выдаётся запрошенная часть документа (см. "Range-Unit" в запросе клиента)
Коды с номером типа 3xx: перенаправление — действие нуждается в уточнении либо просто информационный ответ:
- "300 Multiple Choices" — множественный выбор — по данному запросу обнаружено несколько вариантов документов
- "301 Moved Permanently" — документ переехал
- "302 Found" — найдено
- "303 See Other" — смотри остальные
- "304 Not Modified" — не изменён
- "305 Use Proxy" — используй прокси
- "307 Section" — временное перемещение запроса
Коды с номером типа 4xx: ошибка клиента — запрос клиента имеет либо неправильный синтаксис, либо не понят:
- "400 Bad Request" — плохой запрос
- "401 Unauthorized" — нет авторизации
- "402 Payment Required" — коммерческий ресурс, у вас нет денег на счету
- "403 Forbidden" — запрещение доступа к ресурсу (политика безопасности)
- "404 Not Found" — ресурс не найден
- "405 Method Not Allowed" — метод не поддерживается
- "406 Not Acceptable" — нет доступа к хосту
- "407 Proxy Authentication Required" — для работы с прокси вы должны авторизоваться
- "408 Request Time-out" — слишком долго не было данных с сервера (связь плохая или сервер упал)
- "409 Conflict" — конфликт
- "410 Gone" — процесс идёт (не мешайте)
- "411 Length Required" — требуется длина посылаемых данных
- "412 Precondition Failed" — неправильные умолчания
- "413 Request Entity Too Large" — содержимое запроса слишком велико для этого сервера
- "414 Request-URI Too Large" — слишком длинный адрес запрашиваемого ресурса
- "415 Unsupported Media Type" — в "Accept" не указан поддерживаемый сервером формат данных
- "416 Requested range not satisfiable" — требуемый кусок (с помощью "Range-Unit") имеет неверные размеры
- "417 Expectation Failed" — неожиданная ошибка при разборе запроса (может возникнуть при пересылке типа "multipart/form-data" при неправильном делителе)
Коды с номером типа 5xx: ошибка сервера — сервер не может обработать запрос клиента
- "500 Internal Server Error" — внутренняя ошибка сервера
- "501 Not Implemented" — не применяется (этот запрос не применим)
- "502 Bad Gateway" — "плохие врата" — сервер не обрабатывает запросы с этого сегмента IP
- "503 Service Unavailable" — такой сервис недоступен (к примеру TRACE запрос)
- "504 Gateway Time-out" — слишком долго сервер пытался получить данные, связь плохая
- "505 HTTP Version not supported" — версия HTTP, указанная в запросе, не поддерживается данным сервером
Параметры HTTP ответа
Продолжим разбор параметров заголовка в ответе сервера. Прежде всего упомяну, что параметры "Cache-Control", "Pragma" и "Proxy-Connection" идентичны как для запроса, так и для ответа, по этому всё сказанное про них выше, применимо и тут.
Set-Cookie: name=value; expires=date; path=PATH; domain=HOSTNAME; secure
Не буду подробно останавливаться на этом параметре. Он устанавливает или удаляет cookie и подробно о нём написано в Cookie Specification.
Location: http://www.devresource.org
Данный параметр указывает браузеру, что нужно открыть ресурс http://www.devresource.org вместо текущего. В значении этого параметра указывается URI ресурса для перехода.
Date: Mon, 07 Apr 2003 14:51:19 GMT
Параметр показывает дату документа. Это либо текущая дата (если документ динамический), либо дата создания отправляемого файла. Дата представлена в формате GMT.
Last-Modified: Mon, 07 Apr 2003 14:51:00 GMT
Параметр показывает дату последнего изменения документа.
Server: Apache/1.3.20 (Win32) PHP/4.3.0
Параметр содержит имя сервера.
Keep-Alive: timeout=15, max=100 Connection: Keep-Alive
Эти два параметра сообщают, что поддерживается постоянное соединение с сервером (вы противном случае было бы "Connection: close"), что текущее время timeout для сокета сервера составляет 15 секунд и что клиент может изменить это время максимум до 100 секунд.
Accept-Ranges: bytes
Этот параметр существует, чтобы указать клиенту, какая часть документа ему пересылается (в случае присутствия "Range-Unit" в запросе) Параметр этот может содержать значение "bytes", означающее, что пересылается файл целиком. Так же "none" (или этот параметр может быть просто опущен), означающее, что докачка не используется или не поддерживается, а строка "Accept-Ranges: 1:637" будет означать, что пересылается кусок документа с байта под номером 1 и длиной в 637 байт.
Content-Length: 673
Длина пересылаемого документа.
Content-Type: application/zip
MIME тип пересылаемого документа
Content-Disposition: attachment; filename=test.zip
указывает, что пересылаемый файл имеет название "test.zip"
Accept-Charset: windows-1251
указывает кодировку текста документа (в данном случае — русскую кодировку windows)
Accept-Encoding: compress, gzip
этот параметр используется сервером, чтобы указать клиенту, что документ ему передаётся в сжатом виде (и для сжатия используется стандарт gzip)
Accept-Language: ru
Параметр указывает на язык, использованный сервером для документа. IE6, получивший такой заголовок, к примеру, может переключить раскладку клавиатуры на русскую для текстовых элементов в полученном документе.
Transfer-Encoding: chunked
Данный параметр показывает метод выдачи данных сервером. В данном случае сервер будет выдавать данные по кусочкам, а не всё сразу.
На этом краткое описание HTTP протокола можно считать завершённым.
