Обработка универсальных указателей ресурсов

URL-адрес — это компактное представление расположения и метода доступа для ресурса, расположенного в Интернете. Каждый URL-адрес состоит из схемы (HTTP, HTTPS или FTP) и строки для конкретной схемы. Эта строка также может включать сочетание пути к каталогу, строки поиска или имени ресурса. Функции WinINet позволяют создавать, объединять, разбивать и канонизировать URL-адреса. Дополнительные сведения об URL-адресах см. в статье RFC-1738 в статье Универсальные указатели ресурсов (URL-адреса).

Функции URL-адресов работают в режиме, ориентированном на задачи. Содержимое и формат URL-адреса, предоставленного функции, не проверяются. Вызывающее приложение должно отслеживать использование этих функций, чтобы убедиться, что данные будут иметь нужный формат. Например, функция InternetCanonicalizeUrl преобразует символ "%" в escape-последовательность "%25" при отсутствии флагов. Если в канонизированном URL-адресе используется InternetCanonicalizeUrl , escape-последовательность "%25" будет преобразована в escape-последовательность "%2525", которая будет работать неправильно.

Что такое канонизированный URL-адрес?

Формат всех URL-адресов должен соответствовать принятому синтаксису и семантике для доступа к ресурсам через Интернет. Канонизация — это процесс форматирования URL-адреса с использованием этого допустимого синтаксиса и семантики.

Символы, которые должны быть закодированы, включают все символы, не имеющие соответствующего графического символа в кодировке US-ASCII (шестнадцатеричное значение 80-FF, которые не используются в кодировке US-ASCII, а также шестнадцатеричные 00–1F и 7F, которые являются контрольными символами), пробелы, "%" (который используется для кодирования других символов) и небезопасные символы (<, , ", >#, {, }, |, \, ^, ~, [, ], и ').

Использование функций WinINet для обработки URL-адресов

В следующей таблице перечислены функции URL-адресов.

Функция Описание
InternetCanonicalizeUrl Канонизирует URL-адрес.
InternetCombineUrl Объединяет базовые и относительные URL-адреса.
InternetCrackUrl Анализирует строку URL-адреса на компоненты.
InternetCreateUrl Создает строку URL-адреса из компонентов.
InternetOpenUrl Начинает получение ресурса FTP, HTTP или HTTPS.

 

Канонизация URL-адресов

Канонизация URL-адреса — это процесс, который преобразует URL-адрес, который может содержать небезопасные символы, такие как пробелы, зарезервированные символы и т. д., в допустимый формат.

Функцию InternetCanonicalizeUrl можно использовать для канонизации URL-адресов. Эта функция очень ориентирована на задачи, поэтому приложение должно тщательно отслеживать ее использование. InternetCanonicalizeUrl не проверяет, что переданный ему URL-адрес уже канонизирован и что возвращаемый URL-адрес является допустимым.

Следующие пять флагов определяют, как InternetCanonicalizeUrl обрабатывает определенный URL-адрес. Флаги можно использовать в сочетании. Если флаги не используются, функция кодирует URL-адрес по умолчанию.

Значение Значение
ICU_BROWSER_MODE Не кодируйте и не декодируйте символы после "#" или "?", а также не удаляйте пробелы в конце после "?". Если это значение не указано, кодируется весь URL-адрес и удаляются пробелы в конце.
ICU_DECODE Преобразуйте все последовательности %XX в символы, включая escape-последовательности, перед анализом URL-адреса.
ICU_ENCODE_SPACES_ONLY Кодирование только пробелов.
ICU_NO_ENCODE Не преобразуйте небезопасные символы в escape-последовательности.
ICU_NO_META Не удаляйте мета-последовательности (например, "." и "..") из URL-адреса.

 

Флаг ICU_DECODE следует использовать только для канонических URL-адресов, так как он предполагает, что все последовательности %XX являются escape-кодами, и преобразует их в символы, указанные в коде. Если в URL-адресе есть символ "%", который не является частью escape-кода, ICU_DECODE по-прежнему обрабатывает его как один. Эта характеристика может привести к тому , что InternetCanonicalizeUrl создаст недопустимый URL-адрес.

Чтобы использовать InternetCanonicalizeUrl для возврата полностью декодированного URL-адреса, необходимо указать флаги ICU_DECODE и ICU_NO_ENCODE. Эта настройка предполагает, что URL-адрес, передаваемый в InternetCanonicalizeUrl , ранее был канонизирован.

Объединение базовых и относительных URL-адресов

Относительный URL-адрес — это компактное представление расположения ресурса относительно абсолютного базового URL-адреса. Базовый URL-адрес должен быть известен средству синтаксического анализа и обычно включает схему, сетевое расположение и части ПУТИ URL-адреса. Приложение может вызвать InternetCombineUrl , чтобы объединить относительный URL-адрес с базовым URL-адресом. InternetCombineUrl также канонизирует результирующий URL-адрес.

Взлом URL-адресов

Функция InternetCrackUrl разделяет URL-адрес на части компонентов и возвращает компоненты, указанные структурой URL_COMPONENTS , передаваемой в функцию.

Компоненты, составляющие структуру URL_COMPONENTS , — это номер схемы, имя узла, номер порта, имя пользователя, пароль, URL-путь и дополнительные сведения (например, параметры поиска). Каждый компонент, за исключением номера схемы и порта, имеет строковый элемент, содержащий сведения, и элемент, содержащий длину элемента строки. В схеме и номерах портов есть только элемент, в котором хранится соответствующее значение; Они возвращаются при всех успешных вызовах InternetCrackUrl.

Чтобы получить значение определенного компонента в структуре URL_COMPONENTS , члену, в котором хранится длина строки этого компонента, необходимо задать ненулевое значение. Строковый элемент может быть адресом буфера или значением NULL.

Если элемент указателя содержит адрес буфера, элемент длины строки должен содержать размер этого буфера. InternetCrackUrl возвращает сведения о компоненте в виде строки в буфере и сохраняет длину строки в элементе длины строки.

Если элемент указателя имеет значение NULL, для элемента длины строки можно задать любое ненулевое значение. InternetCrackUrl сохраняет адрес первого символа строки URL-адреса, содержащей сведения о компоненте, и задает длину строки в виде числа символов в оставшейся части строки URL-адреса, относящейся к компоненту.

Для всех членов указателя задано значение NULL с элементом ненулевой длины, указывающим на соответствующую начальную точку в строке URL-адреса. Длина, хранящуюся в элементе length, должна использоваться для определения конца сведений об отдельном компоненте.

Чтобы правильно завершить инициализацию структуры URL_COMPONENTS , члену dwStructSize необходимо задать размер структуры URL_COMPONENTS в байтах.

В следующем примере возвращаются компоненты URL-адреса в поле ввода, IDC_PreOpen1, а компоненты возвращаются в список IDC_PreOpenList. Чтобы отобразить только сведения для отдельного компонента, эта функция копирует символ сразу после сведений о компоненте в строке и временно заменяет его значением NULL.

#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
#include <wininet.h>
#include <stdlib.h>

#pragma comment(lib, "wininet.lib")
#pragma comment(lib, "user32.lib")

#define  CRACKER_BUFFER_SIZE           MAX_PATH

// For sample source code implementing the InternetErrorOut( ) 
// function referenced below, see the "Handling Errors" topic  
// under "Using WinInet"
extern BOOL WINAPI InternetErrorOut( HWND hWnd, DWORD dwError,
                                     LPCTSTR szFailingFunctionName );

// Forward declaration of listUrlPart helper functions:
BOOL listURLpart( HWND hDlg, int nListBoxID, 
                  LPTSTR szPartName, LPTSTR part, DWORD partLength );
BOOL listURLpart( HWND hDlg, int nListBoxID, 
                  LPTSTR szPartName, int partValue );

// Static list describing the URL Scheme types 
// enumerated in INTERNET_SCHEME:
TCHAR* schemeType[] =
{
  TEXT( "[Partial URL]" ),                //  0
  TEXT( "[Unknown scheme]" ),             //  1
  TEXT( "[Default scheme]" ),             //  2
  TEXT( "FTP" ),                          //  3
  TEXT( "Gopher" ),                       //  4
  TEXT( "HTTP" ),                         //  5
  TEXT( "HTTPS" ),                        //  6
  TEXT( "File" ),                         //  7
  TEXT( "News" ),                         //  8
  TEXT( "MailTo" ),                       //  9
  TEXT( "Socks" ),                        // 10
  TEXT( "JavaScript" ),                   // 11
  TEXT( "VBScript" )                      // 12
};
#define  CRACKER_SCHEME_TYPE_ARRAY_SIZE      13

BOOL WINAPI Cracker( HWND hDlg, int nURLtextBoxId, int nListBoxId )
{
   int i, j;
   TCHAR* failedFunctionName;
   TCHAR URL_buffer[CRACKER_BUFFER_SIZE];

   URL_COMPONENTS URLparts;

   URLparts.dwStructSize = sizeof( URLparts );

   // The following elements determine which components are displayed
   URLparts.dwSchemeLength    = 1;
   URLparts.dwHostNameLength  = 1;
   URLparts.dwUserNameLength  = 1;
   URLparts.dwPasswordLength  = 1;
   URLparts.dwUrlPathLength   = 1;
   URLparts.dwExtraInfoLength = 1;

   URLparts.lpszScheme     = NULL;
   URLparts.lpszHostName   = NULL;
   URLparts.lpszUserName   = NULL;
   URLparts.lpszPassword   = NULL;
   URLparts.lpszUrlPath    = NULL;
   URLparts.lpszExtraInfo  = NULL;

   SendDlgItemMessage( hDlg, nListBoxId, LB_RESETCONTENT, 0, 0 );
   if( !GetDlgItemText( hDlg, nURLtextBoxId, 
                        URL_buffer, CRACKER_BUFFER_SIZE ) )
   {
       failedFunctionName = TEXT( "GetDlgItemText" );
       goto CrackerError_01;
   }

   if( FAILED( StringCchLength( URL_buffer, CRACKER_BUFFER_SIZE, 
                                (size_t*) &i ) ) )
   {
       failedFunctionName = TEXT( "StringCchLength" );
       goto CrackerError_01;
   }

   if( !InternetCrackUrl( URL_buffer, (DWORD)_tcslen( URL_buffer ), 0, 
                          &URLparts ) )
   {
       failedFunctionName = TEXT( "InternetCrackUrl" );
       goto CrackerError_01;
   }

   failedFunctionName = TEXT( "listURLpart" );

   i = URLparts.nScheme + 2;
   if( ( i >= 0 ) && ( i < CRACKER_SCHEME_TYPE_ARRAY_SIZE ) )
   {
       StringCchLength( schemeType[i], 
                        CRACKER_BUFFER_SIZE, 
                        (size_t*) &j );
       if( !listURLpart( hDlg, nListBoxId, 
                         TEXT("Scheme type"), 
                         schemeType[i], j ))
           goto CrackerError_01;
   }

   if( !listURLpart( hDlg, nListBoxId, TEXT( "Scheme text" ), 
                     URLparts.lpszScheme, 
                     URLparts.dwSchemeLength ) ||
       !listURLpart( hDlg, nListBoxId, TEXT( "Host name" ), 
                     URLparts.lpszHostName, 
                     URLparts.dwHostNameLength) ||
       !listURLpart( hDlg, nListBoxId, TEXT( "Port number" ), 
                     (int) URLparts.nPort ) ||
       !listURLpart( hDlg, nListBoxId, TEXT( "User name" ), 
                     URLparts.lpszUserName, 
                     URLparts.dwUserNameLength) ||
       !listURLpart( hDlg, nListBoxId, TEXT( "Password" ), 
                     URLparts.lpszPassword, 
                     URLparts.dwPasswordLength) ||
       !listURLpart( hDlg, nListBoxId, TEXT( "Path" ), 
                     URLparts.lpszUrlPath, 
                     URLparts.dwUrlPathLength) ||
       !listURLpart( hDlg, nListBoxId, TEXT( "Extra information"), 
                     URLparts.lpszExtraInfo, 
                     URLparts.dwExtraInfoLength))
           goto CrackerError_01;

   return( TRUE );

CrackerError_01:
// For sample source code of the InternetErrorOut( ) function 
// referenced below, see the "Handling Errors" 
// topic under "Using WinInet"
   InternetErrorOut( hDlg, GetLastError( ), failedFunctionName );
   return FALSE;
}

// listURLpart( ) helper function for string parts
BOOL listURLpart( HWND hDlg, int nListBoxId, 
                  LPTSTR szPartName, LPTSTR part, DWORD partLength )
{
  TCHAR outputBuffer[CRACKER_BUFFER_SIZE];
  LPTSTR nextStart;
  size_t nextSize;

  if( partLength == 0 )  // Just skip empty ones
    return( TRUE );

  if( FAILED( StringCchCopyEx( outputBuffer, 
                              (size_t) CRACKER_BUFFER_SIZE,
                               szPartName, &nextStart, 
                               &nextSize, 0 ) ) ||
      FAILED( StringCchCopyEx( nextStart, nextSize, TEXT( ": " ), 
                               &nextStart, &nextSize, 0 ) ) ||
      FAILED( StringCchCopyNEx( nextStart, nextSize, part, 
                                (size_t) partLength,
                                &nextStart, &nextSize, 0 ) ) )
    return( FALSE );

  *nextStart = 0;
  if( SendDlgItemMessage( hDlg, nListBoxId, LB_ADDSTRING, 0, 
                          (LPARAM)outputBuffer ) < 0 )
    return( FALSE );
  return( TRUE );
}

// listURLpart( ) helper function for numeric parts
BOOL listURLpart( HWND hDlg, int nListBoxId, 
                  LPTSTR szPartName, int partValue )
{
  TCHAR outputBuffer[CRACKER_BUFFER_SIZE];

  if( FAILED( StringCchPrintf( outputBuffer, 
                               (size_t) CRACKER_BUFFER_SIZE,
                               TEXT( "%s: %d" ), szPartName, 
                               partValue ) ) ||
      ( SendDlgItemMessage( hDlg, nListBoxId, LB_ADDSTRING, 0, 
                            (LPARAM)outputBuffer ) < 0 ) )
    return( FALSE );
  return( TRUE );
}

Создание URL-адресов

Функция InternetCreateUrl использует сведения из структуры URL_COMPONENTS для создания универсального указателя ресурсов.

Компоненты, составляющие структуру URL_COMPONENTS , — это схема, имя узла, номер порта, имя пользователя, пароль, URL-путь и дополнительные сведения (например, параметры поиска). Каждый компонент, за исключением номера порта, имеет строковый элемент, содержащий сведения, и элемент, содержащий длину элемента строки.

Для каждого необходимого компонента элемент указателя должен содержать адрес буфера, содержащего сведения. Элемент длины должен быть равен нулю, если элемент указателя содержит адрес строки, завершаемой с нуля; Элемент length должен иметь длину строки, если элемент указателя содержит адрес строки, которая не заканчивается с нуля. Элемент указателя всех компонентов, которые не требуются, должен иметь значение NULL.

Прямой доступ к URL-адресам

Доступ к ресурсам FTP и HTTP в Интернете можно получить напрямую с помощью функций InternetOpenUrl, InternetReadFile и InternetFindNextFile . InternetOpenUrl открывает подключение к ресурсу по URL-адресу, переданном функции. При установке этого подключения можно выполнить два действия. Во-первых, если ресурс является файлом, InternetReadFile может скачать его; Во-вторых, если ресурс является каталогом, InternetFindNextFile может перечислять файлы в каталоге (за исключением случаев, когда используются прокси-серверы CERN). Дополнительные сведения об InternetReadFile см. в разделе Чтение файлов. Дополнительные сведения об InternetFindNextFile см. в разделе Поиск следующего файла.

Для приложений, которым необходимо работать через прокси-сервер CERN, internetOpenUrl можно использовать для доступа к каталогам и файлам FTP. Ftp-запросы упаковываются в виде HTTP-запроса, который будет принимать прокси-сервер CERN.

InternetOpenUrl использует дескриптор HINTERNET , созданный функцией InternetOpen , и URL-адрес ресурса. URL-адрес должен включать схему (http:, ftp:, файл: [для локального файла] или https: [для гипертекстового протокола безопасности]) и сетевое расположение (например www.microsoft.com, ). URL-адрес также может содержать путь (например, /isapi/gomscom.asp? TARGET=/windows/feature/) и имя ресурса (например, default.htm). Для HTTP- или HTTPS-запросов можно включать дополнительные заголовки.

InternetQueryDataAvailable, InternetFindNextFile, InternetReadFile и InternetSetFilePointer (только URL-адреса HTTP или HTTPS) могут использовать дескриптор, созданный InternetOpenUrl для скачивания ресурса.

На следующей схеме показаны дескрипторы для использования с каждой функцией.

дескрипторов для использования с функциями

Корневой дескриптор HINTERNET , созданный InternetOpenUrl , используется internetOpenUrl. Дескриптор HINTERNET, созданный InternetOpenUrl, может использоваться internetQueryDataAvailable, InternetReadFile, InternetFindNextFile (не показано здесь) и InternetSetFilePointer (только URL-адреса HTTP или HTTPS).

Дополнительные сведения см. в разделе HintERNET Handles.

Примечание

WinINet не поддерживает реализации сервера. Кроме того, его не следует использовать из службы. Для серверных реализаций или служб используйте службы Microsoft Windows HTTP (WinHTTP).