sitelink1  
sitelink2  
sitelink3  
sitelink4  
sitelink5  
sitelink6  

HTTPURLCONNECTION를 사용하여 웹 페이지 액세스하기

이 글은 HttpURLConnection 과 이의 서브클래스인 HttpsURLConnection 을 사용하여 보안 웹 페이지에 액세스하는 방법을 보여준다.
또한 비보안 페이지(non-secure page)에서 보안 페이지(secure one)로의 리다이렉트를 쉽게 할 수 있는 방법도 볼 수 있다.
HTTP 와 HTTPS에 관한 정보는 HTTP 1.1 RFC 2616과 HTTPS RFC 2818를 참고하기 바란다.

첫번째 예로, 주어진 URL에 접속하기 위해 다음 WebPageReader 프로그램의 HttpURLConnection 를 이용해 보자.
그리고 페이지의 내용을 스탠다드 아웃(standard out)에 출력하자.

import java.net.URL;
import java.net.MalformedURLException;
import java.net.URLConnection;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class RequestTest {
  private static URLConnection connection;

  private static void connect(String urlString) {
    try {
      URL url = new URL(urlString);
      connection = url.openConnection();
    } catch (MalformedURLException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  private static void readContents() {
    BufferedReader in = null;
    try {
      in = new BufferedReader(
          new InputStreamReader(connection.getInputStream()));
      String inputLine;
      while ((inputLine = in.readLine()) != null) {
        System.out.println(inputLine);
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  public static void main(String[] args) {
    if (args.length != 1) {
      System.err.println("usage: java WebPageReader " + "<url>");
      System.exit(0);
    }
    connect(args[0]);
    readContents();
  }
}


만약 현재의 위치가 방화벽 뒤라면, 다음과 같이 설정된 proxyHost과 proxyPort 변수들이 필요하다.

    http.proxyHost=webcachehttp.proxyPort=8080https.proxyHost=webcachehttps.proxyPort=8080


커맨드 라인에 -D플래그를 이용해서 값을 직접 입력하거나 프로그램 상에서 System.setProperty()를 호출함으로써 변수를 구할 수있다.
WebPageReader 를 컴파일한 후에 이하와 같은 커맨드로 Core Java Technologies Tech Tips 홈 페이지의 내용을 열거해 볼 수 있다.

    java WebPageReader
    http://java.sun.com/developer/JDCTechTips/


URLConnection는 추상 클래스이다.
URL클래스의 openConnection() 메소드는 지정된 URL을 읽을 수 있도록 적절하고 구체적인 서브클래스를 리턴한다.
http나 https URL을 입력하면 이는 HttpURLConnection나 HttpsURLConnection의 서브클래스가 될 것이다.
만약 다음을 openConnection()를 호출하는 라인에 추가하면,

    System.out.println(connection.getClass());


HttpURLConnection의 숨겨진 구현 클래스의 인스턴스를 리턴하는 커넥션(connection)을 보게 된다.
예를 들면, 다음과 같다.

    class sun.net.www.protocol.http.HttpURLConnection


이와 유사하게 보안 페이지를 읽기 위해 동일한 WebPageReader코드를 사용할 수가 있다.

    java WebPageReader https://today.dev.java.net


후자의 경우, 커넥션은 HttpsURLConnection의 서브클래스인 HttpURLConnection타입이라는 것을 알아야 한다.
보다 명확하게 말하자면 다음과 같은 숨어있는 구현 클래스가 있다는 것을 인식할 수 있어야 한다.

    class sun.net.www.protocol.https.HttpsURLConnectionImpl


일반적으로 브라우저에 URL을 입력할 때, 이동하고자하는 페이지가 보안 페이지인지 아닌지는 알 수가 없다.
다시 말하면, today.dev.java.net 페이지를 보기 위해서는 http://today.dev.java.net를 입력한다.
필요하다면 브라우저가 리다이렉트할 것이고 사용자를 https://today.dev.java.net로 연결하기 위해 적절한 신호변경을 수행한다.
WebPageReader프로그램이 요청된 리다이렉션을 수행하는지를 살펴보자.

    java WebPageReader http://today.dev.java.net


원하는 페이지로 리다이렉트되는 대신에 다음과 같은 메시지를 보게 된다.

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<HTML>
<HEAD>
    <TITLE>301 Moved Permanently</TITLE>
</HEAD>
<BODY>
    <H1>Moved Permanently</H1>
    The document has moved
    <A HREF="
https://today.dev.java.net/">here</A>.<P>
    <HR>
    <ADDRESS>
        Apache/1.3.26 Server at today.dev.java.net Port 80
    </ADDRESS>
</BODY>
</HTML>


이 정보로부터 무언가를 읽어내는 것은 어렵지만 문제는 리다이렉션에 관한 것이 아니다.
URL http://linux.java.net으로 프로그램을 실행하면,
프로그램은 http://community.java.net/linux으로 적절히 이를 리다이렉트하고 사용자는 원하는 컨텐츠를 볼 수가 있다.
어떤 일이 일어나는지를 자세히 살펴보려면 HttpURLConnection를 명시적으로 이용해야 한다.
스탠다드 아웃에 웹 페이지의 컨텐츠를 출력하는 코드를 삭제해서 일을 간단히 해보자. RedirectingReader프로그램이다.

import java.net.URL;
import java.net.MalformedURLException;
import java.net.HttpURLConnection;
import java.io.IOException;

public class RedirectingReader {
  private static HttpURLConnection connection;

  private static void connect(String urlString) {
    try {
      URL url = new URL(urlString);
      connection = (HttpURLConnection) url.openConnection();
      System.out.println(connection.getURL());
      System.out.println(connection.getResponseCode() + " "
          + connection.getResponseMessage());
      System.out.println(connection.getURL());
    } catch (MalformedURLException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  public static void main(String[] args) {
    if (args.length != 1) {
      System.err.println("usage: java WebPageReader " + "<url>");
      System.exit(0);
    }
    connect(args[0]);
  }
}


다음과 같이 리다이렉트하는 URL을 입력하여 RedirectingReader 를 컴파일하고 실행해 보자.

    java RedirectingReader http://linux.java.net/


다음과 같은 출력값을 보게 될 것이다.

    http://linux.java.net/
    200 OK
    http://community.java.net/linux/


첫번째 라인이 재빨리 나타난다.
String값으로 URL을 생성하고 HttpURLConnection객체를 실행하자.
그러면 http://linux.java.net를 요청하는 동안에 잠깐의 멈춤이 생긴다.
페이지가 리다이렉트되는 이 시간동안 받게 되는 응답 코드와 메시지는 리다이렉트된 페이지로부터 온 것이다.
이 리다이렉트된 페이지는 출력값의 3번째 라인에 열거된다.

    URL url = new URL(urlString);


아래의 라인을 추가해서 리다이렉션을 허용하지 않았을 때 발생하는 현상을 보자.

    HttpURLConnection.setFollowRedirects(false);


출력값은 다음과 같다.

    http://linux.java.net/
    302 Found
    http://linux.java.net/


이는 302 에러가 발생했다는 것을 나타낸다.
곧 리다이렉트되지 않고 URL이 그대로 유지되었다는 것이다.
이를 처리하기 전에, 좀 전에 추가했던 라인을 지우고 리다이렉션이 다시 작동하는지를 확인하기 위해 RedirectingReader 프로그램을 실행시켜보자.
RedirectingReader를 다시 한번 실행시키고, http://today.dev.java.net 를 입력하자.
다음과 같은 출력값을 보게 된다.

    http://today.dev.java.net
    301 Moved Permanently
    http://today.dev.java.net


위는 이 프로그램이 "Moved Permanently" 에러를 처리할 수 없어서 리다이렉트 했다는 것을 말한다.
이것이 바로 원했던 디폴트 속성이다.
보안문제 때문에 http 과 https간에는 리다이렉트할 수 없다.
어디로 리다이렉트할 것이냐하는 정보는 이 응답의 헤더 부분에 있다.
이전의 요청에 대한 전체 응답 헤더는 다음과 같다.

HTTP/1.1 301 Moved Permanently
Date: Tue, 03 Feb 2004 01:38:43 GMT
Server: Apache/1.3.26 (Unix) mod_ssl/2.8.10
OpenSSL/0.9.6b
mod_jk/1.2.1
Location:
https://today.dev.java.net/
Content-type: text/html; charset=iso-8859-1


HttpURLConnection 내의 getResponseCode() 과 getResponseMessage() 메소드가 이 응답의 첫번째 라인에 포함된 정보를 리턴하는 것을 본 적이 있을 것이다.
혹은 getHeaderField() 메소드를 이용해서 헤더 필드의 이름에 스트링값을 넣어주어도 된다.
가령, getHeaderField("Location")는 https://today.dev.java.net/값을 리턴한다.

이 글의 시작부분에서 언급했던 HTTP 1.1 과 HTTPS RFCs에서 요청과 응답 헤더의 포맷에 관해 자세히 살펴 볼 수 있다.
300 레벨 응답에서, "Location:"는 리다이렉션을 위한 위치값을 제공해야만 한다.
301나 302 에러를 발생하는지를 확인하기 위해 다음 사항을 추가하자.

  private static boolean mustRedirect(int code) {
    if (code == HttpURLConnection.HTTP_MOVED_PERM
        || code == HttpURLConnection.HTTP_MOVED_TEMP) {
      System.out.println("Received error " + code + ", trying secure redirect");
      return true;
    } else
      return false;
  }


리다이렉트가 자동적으로 처리되지 않는다면 사용자는 301 나 302 응답 코드만을 받게 된다는 것을 기억하자.
지금까지 이것은 리다이렉트가 HttpURLConnection가 아닌 HttpsURLConnection가 필요하다는 것을 의미했다.
"Location:" 필드에 제공된 정보를 따르자. 다음과 같이 새로운 정보를 이용해서 url 값과 커넥션을 재설정하자.

    url = new URL("https://" + url.getHost() + url.getFile());    connection = (HttpsURLConnection) url.openConnection();


마지막으로, 웹 리더를 받기 위해 위의 모든 사항을 모으고 필요할 때 보안 페이지로 리다이렉트될 수 있도록 하자.

import javax.net.ssl.HttpsURLConnection;
import java.net.URL;
import java.net.MalformedURLException;
import java.net.HttpURLConnection;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class RedirectingReader {
  private static HttpURLConnection connection;

  private static URL url;

  private static void connect(String urlString) {
    try {
      url = new URL(urlString);
      connection = (HttpURLConnection) url.openConnection();
      int code = connection.getResponseCode();
      if (mustRedirect(code))
        secureRedirect(connection.getHeaderField("Location"));
      readContents();
    } catch (MalformedURLException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  private static boolean mustRedirect(int code) {
    if (code == HttpURLConnection.HTTP_MOVED_PERM
        || code == HttpURLConnection.HTTP_MOVED_TEMP) {
      return true;
    } else
      return false;
  }

  private static void secureRedirect(String location) throws IOException {
    System.out.println(location);
    url = new URL(location);
    connection = (HttpsURLConnection) url.openConnection();
  }

  private static void readContents() {
    BufferedReader in = null;
    try {
      in = new BufferedReader(
          new InputStreamReader(connection.getInputStream()));
      String inputLine;
      while ((inputLine = in.readLine()) != null) {
        System.out.println(inputLine);
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  public static void main(String[] args) {
    if (args.length != 1) {
      System.err.println("usage: java WebPageReader " + "<url>");
      System.exit(0);
    }
    connect(args[0]);
  }
}


업데이트된 RedirectingReader 를 컴파일하고 동작이 제대로 이루어지는지 확인하기 위해 몇 개의 다른 입력값을 넣어 실행해보자.
예를 들면 http://today.dev.java.nethttp://linux.java.net를 넣어보자.
리다이렉트되지 않는 보안페이지와 비보안 페이지를 입력해서 RedirectingReader를 실행해도 동작이 적절하게 이루어져야 한다.
이제 다시 뒤로 돌아가서 최초 WebReader 프로그램에 모든 println() 메소드를 제거하고 readContents()를 추가할 수 있다.

번호 제목 글쓴이 날짜 조회 수
23 Polymorphism과 Method Overriding에 대한 이야기 황제낙엽 2003.05.15 330
22 자바의 특징 황제낙엽 2003.05.06 450
21 JVM (Java Virtual Machine)에 대한 몇가지 설명 황제낙엽 2003.05.06 210
20 자바설화 황제낙엽 2003.05.03 395
19 XML기반 정보 보호 기술의 대두 황제낙엽 2003.04.22 265
18 인터넷을 통한 자바 기술의 변화 황제낙엽 2003.04.07 493
17 JAVA관련 용어와 기술 황제낙엽 2003.04.05 458
16 UTF-8을 위한 문자열 인코딩처리 관련 황제낙엽 2006.10.06 881
15 Code Conventions for JavaTM Programing Language file 황제낙엽 2006.10.23 518
14 자바에서 UTF-8 개발에 관한 정리 (2) 황제낙엽 2006.10.06 543
13 자바에서 UTF-8 개발에 관한 정리 (1) 황제낙엽 2006.10.06 539
12 일본어 전각 반각 변환 예제 소스 .두번째 [1] file 황제낙엽 2007.01.11 569
11 일본어 전각 반각 변환 예제 소스 .첫번째 file 황제낙엽 2007.01.10 3371
10 환경파일 로드 클래스 구성 (XML파싱) 황제낙엽 2007.01.05 437
9 HttpURLConnection을 사용하여 웹 페이지 액세스하기 (두번째) 황제낙엽 2007.01.05 593
8 자바의 I/O 예제 [1] file 황제낙엽 2006.12.28 346
7 HttpURLConnection 클래스를 이용한 Search 황제낙엽 2006.12.28 670
» HttpURLConnection을 사용하여 웹 페이지 액세스하기 (첫번째) 황제낙엽 2006.12.28 522
5 Servlet으로 구현한 파일 다운로드(download) 페이지 황제낙엽 2006.12.27 455
4 JSP로 구현한 파일 다운로드(download) 페이지 황제낙엽 2006.12.27 2583