- port - 로컬에서의 포트
- host - 이 터널링을 이용할 때 호스트가 접속할 주소
- hostport - 이 터널링을 이용할 때 호스트가 접속할 포트
ssh -L 80:127.0.0.1:8080
로 터널링을 성립시킨 후
localhost:80 으로 접속하면,
server address 로 접속한 서버가 127.0.0.1:8080으로 접속한다.
(require 'package)
(add-to-list 'package-archives
'("melpa" . "https://melpa.org/packages/"))
(defun isr-go-mode-hook ()
...
;; Call Gofmt before saving
(add-hook 'before-save-hook 'gofmt-before-save)
...
(add-hook 'go-mode-hook 'isr-go-mode-hook)
go get -u golang.org/x/tools/cmd/...
(defun isr-go-mode-hook ()
...
;; Godef jump key binding
(local-set-key (kbd "M-.") 'go-guru-definition)
(local-set-key (kbd "M-*") 'pop-tag-mark))
;; highlight identifiers
(go-guru-hl-identifier-mode))
...
(add-hook 'go-mode-hook 'isr-go-mode-hook)
golangci-lint 를 설치합니다.
go get -u github.com/golangci/golangci-lint/cmd/golangci-lint
(defun isr-go-mode-hook ()
...
;; flycheck
(flycheck-mode)
...
(add-hook 'go-mode-hook 'isr-go-mode-hook)
go get -u github.com/nsf/gocode
(defun isr-go-mode-hook ()
...
(require 'company)
(require 'company-go)
(set (make-local-variable 'company-backends) '(company-go))
(company-mode)
...
(add-hook 'go-mode-hook 'isr-go-mode-hook)
전체 .emacs 파일을 모으면 다음과 같습니다.
(require 'package)
(add-to-list 'package-archives
'("melpa" . "https://www.melpa.org/packages/"))
(defun isr-go-mode-hook ()
;; flycheck
(flycheck-mode)
;; Call Gofmt before saving
(add-hook 'before-save-hook 'gofmt-before-save)
;; Godef jump key binding
(local-set-key (kbd "M-.") 'go-guru-definition)
(local-set-key (kbd "M-*") 'pop-tag-mark)
;; company-go
(require 'company)
(require 'company-go)
(set (make-local-variable 'company-backends) '(company-go))
(company-mode)
;; highlight identifiers
(go-guru-hl-identifier-mode))
(add-hook 'go-mode-hook 'isr-go-mode-hook)
import "package:googleapis_auth/auth_browser.dart";
...
var id = new ClientId("....apps.googleusercontent.com", null);
var scopes = [...];
// Initialize the browser oauth2 flow functionality.
createImplicitBrowserFlow(id, scopes).then((BrowserOAuth2Flow flow) {
flow.obtainAccessCredentialsViaUserConsent()
.then((AccessCredentials credentials) {
// Credentials are available in [credentials].
...
flow.close();
});
});
import "package:googleapis_auth/auth_browser.dart";
...
var id = new ClientId("....apps.googleusercontent.com", null);
var scopes = [...];
// Initialize the browser oauth2 flow functionality.
createImplicitBrowserFlow(id, scopes).then((BrowserOAuth2Flow flow) {
flow.clientViaUserConsent().then((AuthClient client) {
// Authenticated and auto refreshing client is available in [client].
...
client.close();
flow.close();
});
});
obtainAccessCredentialsViaUserConsent
, clientViaUserConsent
를 호출할 때는 브라우저의 팝업 방지기능에 걸리지 않도록 event handler에서만 호출할 것을 권하는데, 나는 아래와 같은 코드로 왜 팝업 방지기능에 걸리는지 한참을 헤맸었다.
<material-button (trigger)="onClick">Click me</material-button>
void onClick() {
createImplicitBrowserFlow(id, scopes).then((BrowserOAuth2Flow flow) {
flow.clientViaUserConsent().then((AuthClient client) {
// Authenticated and auto refreshing client is available in [client].
...
client.close();
flow.close();
});
});
}
clientViaUserConsent
이 호출되는데 무엇이 문제인가? 꽤 오래 고민하다가 stackoverflow에 나와 같은데서 헤매던 사람의 질문에 대한 답을 보고 겨우 깨달았는데, 중간에 .then 문법이 끼어있다는 점이었다. async 메소드가 호출되었으니 then 이하 구절이 onClick과 같은 시점에 호출된다는 보장이 없었던 것.BrowserOAuth2Flow browserOAuth2Flow = null;
// angulardart 컴퍼넌트의 lifecycle에 따라 호출되는 메소드.
@override
void ngOnInit() {
// Initialize the browser oauth2 flow functionality.
createImplicitBrowserFlow(id, scopes).then((BrowserOAuth2Flow flow) {
browserOAuth2Flow = flow;
});
}
void onClick() {
if (browserOAuth2Flow == null) {
return;
}
browserOAuth2Flow
.obtainAccessCredentialsViaUserConsent()
.then((AccessCredentials credentials) {
// Credentials are available in [credentials].
browserOAuth2Flow.close();
browserOAuth2Flow = null;
});
}