Setting HTTP request headers

공헌자 숫자: 3명

 

HTTP는 웹 내부의 핵심 기술들 중의 하나입니다. 실제 컨텐츠 외에도 HTTP 헤더를 통해 중요한 정보들이 HTTP 요청과 응답에 포함되어 전송됩니다.

여러분은 응용프로그램이 생성하는 요청에 고유한 HTTP 헤더를 추가할 수 있는데, 이러한 요청들은 XMLHttpRequest로 인해서 HTTP 채널을 명시적으로 여는 코드로부터 컨테츠 내의 <img> 요소에 대한 요청, CSS로 부터 발생하는 요청 등을 모두 포함합니다.

HTTP Channels

HTTP 요청과 응답을 처리하기 위해서는 일반적으로 nsIHttpChannel을 사용하게 됩니다. nsIHttpChannel 인터페이스는 많은 속성과 메소드를 포함하고 있지만, 우리가 관심을 가질 메소드는 setRequestHeader입니다. 이 메소드는 우리가 HTTP 요청 헤더를 설정할 수 있게 해줍니다.

아래는 HTTP 헤더를 설정하는 예제 코드입니다.

// "X-Hello: World" 헤더를 요청에 넣습니다.
httpChannel.setRequestHeader("X-Hello", "World", false);

위 예제 코드에는 httpChannel라는 변수가 있는데, 이는 nsIHttpChannel를 구현하는 객체입니다(변수 이름은 아무거나 상관없습니다).

setRequestHeader 메소드는 3개의 인자를 가집니다. 첫 번째 인자는 HTTP 요청 헤더의 이름입니다. 두번째 인자는 HTTP 요청 헤더의 입니다. 그리고 마지막 세번째 인자는 일단 무시하고 항상 false로 지정하세요.

위 예제에서 우리가 추가한 HTTP 요청 헤더는 이름X-Hello이고 World입니다.

주의: 만일 고유한 HTTP 헤더를 만들 경우, 반드시 이름 앞에 X-를 붙이세요(예제에서는 이름 앞에 X-를 붙여서 Hello가 아닌 X-Hello인 HTTP 헤더를 만들었습니다).

알림(notifications)

이제 여러분은 HTTP 요청이 만들어질 때 어떻게 nsIHttpChannel을 얻는지에 대해 궁금하실 것입니다.

여러분의 코드가 요청을 초기화하는 경우에는, 이미 이 객체를 가지고 있게 됩니다. 다른 요청을 가로챌(trapping) 경우에는 알림(notification)의 방식으로 처리되며, 이는 다른 언어와 프레임워크에서 볼 수 있는 events 또는 signals과 유사합니다.

특별히 HTTP 요청이 일어나기 직전에 nsIHttpChannel을 얻기 위해서는 "http-on-modify-request" 토픽(topic)을 관찰(observe)해야 합니다. ("http-on-modify-request"은 문자열입니다.)

주의: "http-on-modify-request"외에도 여러가지 토픽이 존재하며, "http-on-examine-response" , "xpcom-shutdown"와 같은 알림을 받을 수 있습니다. 또한 자신만의 토픽을 만들고 알림을 발송할 수 있습니다.

알림 프레임워크와 일반적인 알림 토픽의 목록에 관한 정보는 Observer Notifications을 참조하세요.

관찰자(observers)

"http-on-modify-request"와 같은 토픽에 대한 알림을 받으려면, 관찰자(observer)를 생성해야 합니다. 관찰자는 nsIObserver 인터페이스를 구현하는 컴포넌트입니다. 일단 관찰자에 토픽을 등록하면, 관찰자는 자신의 observe 메소드가 호출됨으로써 등록한 토픽에 대한 알림을 받게 됩니다.

아래는 관찰자 예제로 http-on-modify-request 알림으로 전송된 채널에 사용자 정의 헤더인 "X-Hello"를 추가합니다.

var httpRequestObserver =
{
  observe: function(subject, topic, data) 
  {
    if (topic == "http-on-modify-request") {
      var httpChannel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
      httpChannel.setRequestHeader("X-Hello", "World", false);
    }
  }
};

div class="note"> Doesn't seem very suitable for this article; readers should are typically assumed to be familiar with JS. Nickolay '''NOTE''': Often people expect JavaScript to be just like Java. And while superficially, they look very similar, there are some important differences between the two. For example, while Java is an <em>object-oriented programming language</em>, JavaScript is not. JavaScript is <em>prototype-based programming language</em> and as such while it has <em>objects</em> it does not have <em>classes</em>. (Which is why, if you are not well versed with JavaScript, the object creation in the sample code above may look strange.) </div

observe 메소드가 가지는 파라미터의 개수가 중요하다는 점을 명심하세요. 이 메소드는 위 예제 코드에서 본 바와 같이 3개의 파라미터를 가집니다. "http-on-modify-request" 토픽의 경우, 첫번째 파라미터(위 코드에서 subject)는 nsIHttpChannel이 됩니다. 그러나 우리에게는 nsISupports로 전달됩니다. 따라서 이 파라미터를 QueryInterface를 통하여 nsISupports에서 nsIHttpChannel변경할 필요가 있습니다. 당연히, 어떤 객체를 다른것으로 변환하는 것은 매우 보기 않좋은 것이고, syntactic sugar가 떨어지게 됩니다.

if 블럭 내 두 번째 줄은 아마 친근할 것입니다. 본 기사의 앞에서 사용한 것과 동일한 코드로 HTTP 요청 헤더를 추가하는 것입니다.

httpRequestObserver라는 객체의 이름은 중요하지 않습니다. 이 이름은 우리가 원하는데로 지정할 수 있습니다.

등록(registering)

관찰자(observer)를 생성한 뒤에는 이를 등록해 주어야 합니다. 우리의 경우에 "http-on-modify-request" 토픽에 등록하고자 합니다. 아래의 코드로 이를 수행할 수 있습니다.

var observerService = Components.classes["@mozilla.org/observer-service;1"]
                                .getService(Components.interfaces.nsIObserverService);
observerService.addObserver(httpRequestObserver, "http-on-modify-request", false);

첫번째 문장에서는 알림을 받고자하는 토픽을 등록할 객체를 얻습니다. 즉, 관찰자 객체를 생성합니다.

두번째 문장에서 실제 등록을 합니다. 이 코드는 "http-on-modify-request" 토픽이 발생하면(이 토픽은 HTTP 요청의 바로 전에 발생한다는 것을 알고 있습니다) httpRequestObserver에게 (observe 메소드를 실행함으로써) 알려달라고 말하는 것입니다.

등록 해제(unregistering)

여러분은 종료 전에 관측자의 등록을 해제하는게 좋습니다. 그렇지 않으면 메모리 누수가 발생할 수 있습니다. 관측자의 등록을 해제하려면 다음과 같이 nsIObserverService.removeObserver를 사용하면 됩니다.

observerService.removeObserver(httpRequestObserver, "http-on-modify-request");

XPCOM components

여러분은 (창이 아니라)응용 프로그램마다 http-on-modify-request 관찰자를 등록해주어야 합니다. 이는 관찰자의 구현을 overlay가 아닌 XPCOM component에서 해야 한다는 것을 의미합니다.

요약

기본적인 사항은 여기까지입니다. 하지만 여러분을 위해 httpRequestObserver 객체를 이용하는 약간 다른 버전을 보여 주겠습니다.

앞서 보았던 버전은 학습을 위해서는 유용하지만, 실제 응용 프로그램에서는 다음과 같이 코딩될 것입니다.

var httpRequestObserver =
{
  observe: function(subject, topic, data)
  {
    if (topic == "http-on-modify-request") {
      var httpChannel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
      httpChannel.setRequestHeader("X-Hello", "World", false);
    }
  },

  get observerService() {
    return Components.classes["@mozilla.org/observer-service;1"]
                     .getService(Components.interfaces.nsIObserverService);
  },

  register: function()
  {
    this.observerService.addObserver(this, "http-on-modify-request", false);
  },

  unregister: function()
  {
    this.observerService.removeObserver(this, "http-on-modify-request");
  }
};

이 객체는 편의를 위해 register()unregister() 메소드가 있으므로 다음과 같이 호출해서 활성화할 수 있습니다.

httpRequestObserver.register();

또한 프로그램이 종료되는 시점에 관찰자를 등록 해제하는 것을 잊지 마세요.

httpRequestObserver.unregister();

여기까지 입니다.

We need downloadable XPCOM component for this

예제 코드

var headerName  = "X-hello";
var headerValue = "world";

function LOG(text)
{
    //    var consoleService = Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService);
    //    consoleService.logStringMessage(text);
}

function myHTTPListener() { }

myHTTPListener.prototype = {
    
  observe: function(subject, topic, data)
  {
      if (topic == "http-on-modify-request") {

          LOG("----------------------------> (" + subject + ") mod request");

          var httpChannel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
          httpChannel.setRequestHeader(headerName, headerValue, false);
          return;
      }


      if (topic == "app-startup") {

          LOG("----------------------------> app-startup");

          var os = Components.classes["@mozilla.org/observer-service;1"]
                             .getService(Components.interfaces.nsIObserverService);

          os.addObserver(this, "http-on-modify-request", false);
          return;
      }
  },
 
  QueryInterface: function (iid) {
        if (iid.equals(Components.interfaces.nsIObserver) ||
            iid.equals(Components.interfaces.nsISupports))
            return this;
        
        Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
        return null;
    },
};

var myModule = {
    registerSelf: function (compMgr, fileSpec, location, type) {

        var compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
        compMgr.registerFactoryLocation(this.myCID,
                                        this.myName,
                                        this.myProgID,
                                        fileSpec,
                                        location,
                                        type);


          LOG("----------------------------> registerSelf");

        var catMgr = Components.classes["@mozilla.org/categorymanager;1"].getService(Components.interfaces.nsICategoryManager);
        catMgr.addCategoryEntry("app-startup", this.myName, this.myProgID, true, true);
    },


    getClassObject: function (compMgr, cid, iid) {

          LOG("----------------------------> getClassObject");

        return this.myFactory;
    },

    myCID: Components.ID("{9cf5f3df-2505-42dd-9094-c1631bd1be1c}"),

    myProgID: "@dougt/myHTTPListener;1",

    myName:   "Simple HTTP Listener",

    myFactory: {
        QueryInterface: function (aIID) {
            if (!aIID.equals(Components.interfaces.nsISupports) &&
                !aIID.equals(Components.interfaces.nsIFactory))
                throw Components.results.NS_ERROR_NO_INTERFACE;
            return this;
        },

        createInstance: function (outer, iid) {

          LOG("----------------------------> createInstance");

          return new myHTTPListener();
        }
    },

    canUnload: function(compMgr) {
        return true;
    }
};

function NSGetModule(compMgr, fileSpec) {
    return myModule;
}

 

문서 태그 및 공헌자

Contributors to this page: teoli, fscholz, Suguni
최종 변경: teoli,