diff --git a/contrib/endpoints/src/api_manager/context/service_context.cc b/contrib/endpoints/src/api_manager/context/service_context.cc index b9f753f0625..d8fc9dfc409 100644 --- a/contrib/endpoints/src/api_manager/context/service_context.cc +++ b/contrib/endpoints/src/api_manager/context/service_context.cc @@ -41,6 +41,9 @@ const double kDefaultTraceSampleQps = 0.1; // The time window to send intermediate report for Grpc streaming (second). // Default to 10s. const int kIntermediateReportInterval = 10; + +const char kHTTPHeadMethod[] = "HEAD"; +const char kHTTPGetMethod[] = "GET"; } ServiceContext::ServiceContext(std::unique_ptr env, @@ -74,7 +77,15 @@ MethodCallInfo ServiceContext::GetMethodCallInfo( if (config_ == nullptr) { return MethodCallInfo(); } - return config_->GetMethodCallInfo(http_method, url, query_params); + MethodCallInfo method_call_info = + config_->GetMethodCallInfo(http_method, url, query_params); + // HEAD should be treated as GET unless it is specified from service_config. + if (method_call_info.method_info == nullptr && + http_method == kHTTPHeadMethod) { + method_call_info = + config_->GetMethodCallInfo(kHTTPGetMethod, url, query_params); + } + return method_call_info; } const std::string& ServiceContext::project_id() const { diff --git a/contrib/tools/server/Makefile b/contrib/tools/server/Makefile new file mode 100644 index 00000000000..d7c7d758686 --- /dev/null +++ b/contrib/tools/server/Makefile @@ -0,0 +1,6 @@ +server: + $(CC) -g -o server server.c + +clean: + rm server + diff --git a/contrib/tools/server/README.md b/contrib/tools/server/README.md new file mode 100644 index 00000000000..f962c2c640b --- /dev/null +++ b/contrib/tools/server/README.md @@ -0,0 +1,25 @@ +# Sample TCP Server + +This is a simple TCP server that listens on the specified port (3490 by default) and replies to incoming connections by providing info about src/destination ip/port. + +It demonstrates the use of the getsockopt() system call with the ORIGINAL_DST option to retrieve the original destination ip/port after an iptables redirect. + +So, for example, if you have the server listening on port 3490 on the local machine and an iptables rule like: + +``` +iptables -t nat -I OUTPUT 1 -p tcp --dport 4000:5000 -j REDIRECT --to-port 3490 +``` +your will see: + +``` +$ telnet localhost 3490 +FROM 127.0.0.1:44978, TO 127.0.0.1:3490, ORIG DEST 127.0.0.1:3490 + +$ telnet localhost 4100 +FROM 127.0.0.1:35476, TO 127.0.0.1:3490, ORIG DEST 127.0.0.1:4100 + +$ telnet 1.1.1.1 5000 +FROM 100.100.100.100:60275, TO 127.0.0.1:3490, ORIG DEST 1.1.1.1:5000 +``` + + diff --git a/contrib/tools/server/server.c b/contrib/tools/server/server.c new file mode 100644 index 00000000000..77a2d97e004 --- /dev/null +++ b/contrib/tools/server/server.c @@ -0,0 +1,169 @@ +/* +** server.c + * Demo server to check that we can extract all parameters from an incoming +*request + * and respond to it +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_PORT \ + "3490" // the port users will be connecting to, if not specified on command + // line + +#define QUEUE_SIZE 10 // pending connections queue size + +void sigchld_handler(int s) { + int saved_errno = errno; + + while (waitpid(-1, NULL, WNOHANG) > 0) + ; + + errno = saved_errno; +} + +int main(int argc, char *argv[]) { + struct sigaction sa; + const int yes = 1; + char *port = DEFAULT_PORT; + int rv; + + if (argc > 1) { + port = argv[1]; + } + + struct addrinfo hints; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; // use my IP + + struct addrinfo *server_info = NULL; + if ((rv = getaddrinfo(NULL, port, &hints, &server_info)) != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); + return 1; + } + + struct addrinfo *p; + int sockfd; // listen on sock_fd + for (p = server_info; p != NULL; p = p->ai_next) { + if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { + perror("server: socket"); + continue; + } + + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { + perror("setsockopt"); + exit(1); + } + + if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { + close(sockfd); + perror("server: bind"); + continue; + } + + break; + } + + freeaddrinfo(server_info); + + if (p == NULL) { + fprintf(stderr, "server: failed to bind\n"); + exit(1); + } + if (listen(sockfd, QUEUE_SIZE) == -1) { + perror("listen"); + exit(1); + } + + sa.sa_handler = sigchld_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + if (sigaction(SIGCHLD, &sa, NULL) == -1) { + perror("sigaction"); + exit(1); + } + + char bind_addr_str[INET6_ADDRSTRLEN] = {0}; + struct sockaddr_in *bind_sock_addr = (struct sockaddr_in *)p->ai_addr; + inet_ntop(p->ai_family, &bind_sock_addr->sin_addr, bind_addr_str, + p->ai_addrlen); + printf("server %s: waiting for connections on port %s:%u...\n", port, + bind_addr_str, ntohs(bind_sock_addr->sin_port)); + + while (1) { + socklen_t addr_len = sizeof(struct sockaddr_storage); + int addr_str_len = INET6_ADDRSTRLEN; + + struct sockaddr_storage their_addr = { + 0}; // connector's address information + int accepted_fd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_len); + if (accepted_fd == -1) { + perror("accept"); + continue; + } + + struct sockaddr_in *src_sock_addr = (struct sockaddr_in *)&their_addr; + char their_addr_str[INET6_ADDRSTRLEN] = {0}; + inet_ntop(their_addr.ss_family, &src_sock_addr->sin_addr, their_addr_str, + addr_str_len); + printf("server %s: got connection FROM %s:%u\n", port, their_addr_str, + ntohs(src_sock_addr->sin_port)); + + struct sockaddr_storage my_addr = {0}; // my address information + struct sockaddr_in *dst_sock_addr = (struct sockaddr_in *)&my_addr; + char my_addr_str[INET6_ADDRSTRLEN] = {0}; + getsockname(accepted_fd, (struct sockaddr *)dst_sock_addr, &addr_len); + inet_ntop(my_addr.ss_family, &dst_sock_addr->sin_addr, my_addr_str, + addr_str_len); + printf("server %s: got connection TO %s:%u\n", port, my_addr_str, + ntohs(dst_sock_addr->sin_port)); + + struct sockaddr_storage orig_addr = {0}; // orig address information + struct sockaddr_in *orig_sock_addr = (struct sockaddr_in *)&orig_addr; + char orig_addr_str[INET6_ADDRSTRLEN] = {0}; + int status = getsockopt(accepted_fd, SOL_IP, SO_ORIGINAL_DST, + orig_sock_addr, &addr_len); + + if (status == 0) { + inet_ntop(orig_addr.ss_family, &orig_sock_addr->sin_addr, orig_addr_str, + addr_str_len); + printf("server %s: ORIG DEST %s:%u\n", port, orig_addr_str, + ntohs(orig_sock_addr->sin_port)); + } else { + printf("Could not get orig destination from accepted socket.\n"); + } + + if (!fork()) { // this is the child process + + close(sockfd); + char msg[256] = {0}; + snprintf(msg, 256, "FROM %s:%u, TO %s:%u, ORIG DEST %s:%u\n", + their_addr_str, ntohs(src_sock_addr->sin_port), my_addr_str, + ntohs(dst_sock_addr->sin_port), orig_addr_str, + ntohs(orig_sock_addr->sin_port)); + + if (send(accepted_fd, msg, strlen(msg), 0) == -1) { + perror("send"); + } + close(accepted_fd); + exit(0); + } + close(accepted_fd); + } + + return 0; +} diff --git a/src/envoy/prototype/BUILD b/src/envoy/prototype/BUILD index e5cbaeba10c..258f2046cd7 100644 --- a/src/envoy/prototype/BUILD +++ b/src/envoy/prototype/BUILD @@ -15,6 +15,8 @@ ################################################################################ # +package(default_visibility = ["//visibility:public"]) + cc_binary( name = "envoy_esp", srcs = [ diff --git a/src/envoy/prototype/README.md b/src/envoy/prototype/README.md index 2ddbe98459c..00e73a169d6 100644 --- a/src/envoy/prototype/README.md +++ b/src/envoy/prototype/README.md @@ -36,6 +36,19 @@ This Proxy will use Envoy and talk to Mixer server. go run echo.go ``` +* Modify your iptables: + +``` + sudo iptables -t nat -A OUTPUT -p tcp --dport 9090 -j REDIRECT --to-port 9092 +``` + +Once you are done, you should remove this rule: + +``` + sudo iptables -t nat -D OUTPUT -p tcp --dport 9090 -j REDIRECT --to-port 9092 +``` + + * Start Envoy proxy, run ``` diff --git a/src/envoy/prototype/envoy-esp.conf b/src/envoy/prototype/envoy-esp.conf index dff3bd67b1a..11690bed565 100644 --- a/src/envoy/prototype/envoy-esp.conf +++ b/src/envoy/prototype/envoy-esp.conf @@ -1,7 +1,14 @@ { "listeners": [ + { + "port": 9092, + "bind_to_port": true, + "use_original_dst": true, + "filters": [] + }, { "port": 9090, + "bind_to_port": false, "filters": [ { "type": "read", @@ -26,7 +33,7 @@ }, "access_log": [ { - "path": "/tmp/access.envoy" + "path": "/dev/stdout" } ], "filters": [ @@ -50,7 +57,7 @@ } ], "admin": { - "access_log_path": "/tmp/access.envoy", + "access_log_path": "/dev/stdout", "port": 9001 }, "cluster_manager": { diff --git a/src/envoy/repositories.bzl b/src/envoy/repositories.bzl index 56fabce07ac..dd36a23d456 100644 --- a/src/envoy/repositories.bzl +++ b/src/envoy/repositories.bzl @@ -629,6 +629,6 @@ cc_test( native.new_git_repository( name = "envoy_git", remote = "https://github.com/lyft/envoy.git", - commit = "6b1336a786ebe56c45a1a349ddf706e0526c1ec1", # 2017-01-03 + commit = "c36bb76e304ab4c5584a07303e2953a170888319", build_file_content = BUILD, ) diff --git a/test/backend/echo/echo.go b/test/backend/echo/echo.go index 51aaa07e353..ef13f515ff1 100644 --- a/test/backend/echo/echo.go +++ b/test/backend/echo/echo.go @@ -23,19 +23,22 @@ import ( "io/ioutil" "net/http" "strconv" - "sync" - "time" ) var ( port = flag.Int("port", 8080, "default http port") - mu sync.Mutex requests = 0 data = 0 ) func handler(w http.ResponseWriter, r *http.Request) { + fmt.Printf("%v %v %v %v\n", r.Method, r.URL, r.Proto, r.RemoteAddr) + for name, headers := range r.Header { + for _, h := range headers { + fmt.Printf("%v: %v\n", name, h) + } + } body, err := ioutil.ReadAll(r.Body) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) @@ -51,24 +54,14 @@ func handler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write(body) - mu.Lock() requests++ data += len(body) - defer mu.Unlock() + fmt.Printf("Requests Requests: %v Data: %v\n", requests, data) } func main() { flag.Parse() - go func() { - for { - mu.Lock() - fmt.Printf("Requests Requests: %v Data: %v\n", requests, data) - mu.Unlock() - time.Sleep(time.Second) - } - }() - fmt.Printf("Listening on port %v\n", *port) http.HandleFunc("/", handler)