func (r *RedirectHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { parts := splitPath(req.URL.Path) if len(parts) != 2 || req.Method != "GET" { notFound(w, req) return } resourceName := parts[0] id := parts[1] storage, ok := r.storage[resourceName] if !ok { httplog.LogOf(w).Addf("'%v' has no storage object", resourceName) notFound(w, req) return } redirector, ok := storage.(Redirector) if !ok { httplog.LogOf(w).Addf("'%v' is not a redirector", resourceName) notFound(w, req) return } location, err := redirector.ResourceLocation(id) if err != nil { status := errToAPIStatus(err) writeJSON(status.Code, r.codec, status, w) return } w.Header().Set("Location", location) w.WriteHeader(http.StatusTemporaryRedirect) }
func (r *RedirectHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { var verb string var apiResource string var httpCode int reqStart := time.Now() defer monitor("redirect", &verb, &apiResource, &httpCode, reqStart) requestInfo, err := r.apiRequestInfoResolver.GetAPIRequestInfo(req) if err != nil { notFound(w, req) httpCode = http.StatusNotFound return } verb = requestInfo.Verb resource, parts := requestInfo.Resource, requestInfo.Parts ctx, ok := r.context.Get(req) if !ok { ctx = api.NewContext() } ctx = api.WithNamespace(ctx, requestInfo.Namespace) // redirection requires /resource/resourceName path parts if len(parts) != 2 || req.Method != "GET" { notFound(w, req) httpCode = http.StatusNotFound return } id := parts[1] storage, ok := r.storage[resource] if !ok { httplog.LogOf(req, w).Addf("'%v' has no storage object", resource) notFound(w, req) httpCode = http.StatusNotFound return } apiResource = resource redirector, ok := storage.(Redirector) if !ok { httplog.LogOf(req, w).Addf("'%v' is not a redirector", resource) httpCode = errorJSON(errors.NewMethodNotSupported(resource, "redirect"), r.codec, w) return } location, err := redirector.ResourceLocation(ctx, id) if err != nil { status := errToAPIStatus(err) writeJSON(status.Code, r.codec, status, w) httpCode = status.Code return } w.Header().Set("Location", fmt.Sprintf("http://%s", location)) w.WriteHeader(http.StatusTemporaryRedirect) httpCode = http.StatusTemporaryRedirect }
func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { parts := strings.SplitN(req.URL.Path, "/", 3) if len(parts) < 2 { notFound(w, req) return } resourceName := parts[0] id := parts[1] rest := "" if len(parts) == 3 { rest = parts[2] } storage, ok := r.storage[resourceName] if !ok { httplog.LogOf(w).Addf("'%v' has no storage object", resourceName) notFound(w, req) return } redirector, ok := storage.(Redirector) if !ok { httplog.LogOf(w).Addf("'%v' is not a redirector", resourceName) notFound(w, req) return } location, err := redirector.ResourceLocation(id) if err != nil { status := errToAPIStatus(err) writeJSON(status.Code, r.codec, status, w) return } destURL, err := url.Parse(location) if err != nil { status := errToAPIStatus(err) writeJSON(status.Code, r.codec, status, w) return } destURL.Path = rest destURL.RawQuery = req.URL.RawQuery newReq, err := http.NewRequest(req.Method, destURL.String(), req.Body) if err != nil { glog.Errorf("Failed to create request: %s", err) } newReq.Header = req.Header proxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: "http", Host: destURL.Host}) proxy.Transport = &proxyTransport{ proxyScheme: req.URL.Scheme, proxyHost: req.URL.Host, proxyPathPrepend: path.Join(r.prefix, resourceName, id), } proxy.ServeHTTP(w, newReq) }
func (r *RedirectHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { namespace, kind, parts, err := KindAndNamespace(req) if err != nil { notFound(w, req) return } ctx := api.WithNamespace(api.NewContext(), namespace) // redirection requires /kind/resourceName path parts if len(parts) != 2 || req.Method != "GET" { notFound(w, req) return } id := parts[1] storage, ok := r.storage[kind] if !ok { httplog.LogOf(req, w).Addf("'%v' has no storage object", kind) notFound(w, req) return } redirector, ok := storage.(Redirector) if !ok { httplog.LogOf(req, w).Addf("'%v' is not a redirector", kind) errorJSON(errors.NewMethodNotSupported(kind, "redirect"), r.codec, w) return } location, err := redirector.ResourceLocation(ctx, id) if err != nil { status := errToAPIStatus(err) writeJSON(status.Code, r.codec, status, w) return } w.Header().Set("Location", location) w.WriteHeader(http.StatusTemporaryRedirect) }
// ServeHTTP handles requests to all RESTStorage objects. func (h *RESTHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { parts := splitPath(req.URL.Path) if len(parts) < 1 { notFound(w, req) return } storage := h.storage[parts[0]] if storage == nil { httplog.LogOf(req, w).Addf("'%v' has no storage object", parts[0]) notFound(w, req) return } h.handleRESTStorage(parts, req, w, storage) }
// ServeHTTP serves a series of JSON encoded events via straight HTTP with // Transfer-Encoding: chunked. func (self *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { loggedW := httplog.LogOf(req, w) w = httplog.Unlogged(w) cn, ok := w.(http.CloseNotifier) if !ok { loggedW.Addf("unable to get CloseNotifier") http.NotFound(w, req) return } flusher, ok := w.(http.Flusher) if !ok { loggedW.Addf("unable to get Flusher") http.NotFound(w, req) return } w.Header().Set("Transfer-Encoding", "chunked") w.WriteHeader(http.StatusOK) flusher.Flush() encoder := json.NewEncoder(w) for { select { case <-cn.CloseNotify(): self.watching.Stop() return case event, ok := <-self.watching.ResultChan(): if !ok { // End of results. return } obj, err := api.NewJSONWatchEvent(self.codec, event) if err != nil { // Client disconnect. self.watching.Stop() return } if err := encoder.Encode(obj); err != nil { // Client disconnect. self.watching.Stop() return } flusher.Flush() } } }
// ServeHTTP serves a series of JSON encoded events via straight HTTP with // Transfer-Encoding: chunked. func (self *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { loggedW := httplog.LogOf(w) w = httplog.Unlogged(w) cn, ok := w.(http.CloseNotifier) if !ok { loggedW.Addf("unable to get CloseNotifier") http.NotFound(loggedW, req) return } flusher, ok := w.(http.Flusher) if !ok { loggedW.Addf("unable to get Flusher") http.NotFound(loggedW, req) return } loggedW.Header().Set("Transfer-Encoding", "chunked") loggedW.WriteHeader(http.StatusOK) flusher.Flush() encoder := json.NewEncoder(w) for { select { case <-cn.CloseNotify(): self.watching.Stop() return case event, ok := <-self.watching.ResultChan(): if !ok { // End of results. return } err := encoder.Encode(&api.WatchEvent{ Type: event.Type, Object: api.APIObject{event.Object}, }) if err != nil { // Client disconnect. self.watching.Stop() return } flusher.Flush() } } }
// handleREST handles requests to all our RESTStorage objects. func (s *APIServer) handleREST(w http.ResponseWriter, req *http.Request) { if !strings.HasPrefix(req.URL.Path, s.prefix) { notFound(w, req) return } requestParts := strings.Split(req.URL.Path[len(s.prefix):], "/")[1:] if len(requestParts) < 1 { notFound(w, req) return } storage := s.storage[requestParts[0]] if storage == nil { httplog.LogOf(w).Addf("'%v' has no storage object", requestParts[0]) notFound(w, req) return } s.handleRESTStorage(requestParts, req, w, storage) }
// ServeHTTP serves a series of JSON encoded events via straight HTTP with // Transfer-Encoding: chunked. func (self *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { loggedW := httplog.LogOf(req, w) w = httplog.Unlogged(w) timeoutCh, cleanup := self.t.TimeoutCh() defer cleanup() defer self.watching.Stop() cn, ok := w.(http.CloseNotifier) if !ok { loggedW.Addf("unable to get CloseNotifier") http.NotFound(w, req) return } flusher, ok := w.(http.Flusher) if !ok { loggedW.Addf("unable to get Flusher") http.NotFound(w, req) return } w.Header().Set("Transfer-Encoding", "chunked") w.WriteHeader(http.StatusOK) flusher.Flush() encoder := watchjson.NewEncoder(w, self.codec) for { select { case <-cn.CloseNotify(): return case <-timeoutCh: return case event, ok := <-self.watching.ResultChan(): if !ok { // End of results. return } self.fixup(event.Object) if err := encoder.Encode(&event); err != nil { // Client disconnect. return } flusher.Flush() } } }
// finishReq finishes up a request, waiting until the operation finishes or, after a timeout, creating an // Operation to receive the result and returning its ID down the writer. func (s *APIServer) finishReq(op *Operation, w http.ResponseWriter) { obj, complete := op.StatusOrResult() if complete { status := http.StatusOK switch stat := obj.(type) { case api.Status: httplog.LogOf(w).Addf("programmer error: use *api.Status as a result, not api.Status.") if stat.Code != 0 { status = stat.Code } case *api.Status: if stat.Code != 0 { status = stat.Code } } writeJSON(status, obj, w) } else { writeJSON(http.StatusAccepted, obj, w) } }
// finishReq finishes up a request, waiting until the operation finishes or, after a timeout, creating an // Operation to recieve the result and returning its ID down the writer. func (server *APIServer) finishReq(out <-chan interface{}, sync bool, timeout time.Duration, w http.ResponseWriter) { op := server.ops.NewOperation(out) if sync { op.WaitFor(timeout) } obj, complete := op.StatusOrResult() if complete { status := http.StatusOK switch stat := obj.(type) { case api.Status: httplog.LogOf(w).Addf("programmer error: use *api.Status as a result, not api.Status.") if stat.Code != 0 { status = stat.Code } case *api.Status: if stat.Code != 0 { status = stat.Code } } server.write(status, obj, w) } else { server.write(http.StatusAccepted, obj, w) } }
func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { var verb string var apiResource string var httpCode int reqStart := time.Now() defer monitor("proxy", &verb, &apiResource, &httpCode, reqStart) requestInfo, err := r.apiRequestInfoResolver.GetAPIRequestInfo(req) if err != nil { notFound(w, req) httpCode = http.StatusNotFound return } verb = requestInfo.Verb namespace, resource, parts := requestInfo.Namespace, requestInfo.Resource, requestInfo.Parts ctx, ok := r.context.Get(req) if !ok { ctx = api.NewContext() } ctx = api.WithNamespace(ctx, namespace) if len(parts) < 2 { notFound(w, req) httpCode = http.StatusNotFound return } id := parts[1] rest := "" if len(parts) > 2 { proxyParts := parts[2:] rest = strings.Join(proxyParts, "/") if strings.HasSuffix(req.URL.Path, "/") { // The original path had a trailing slash, which has been stripped // by KindAndNamespace(). We should add it back because some // servers (like etcd) require it. rest = rest + "/" } } storage, ok := r.storage[resource] if !ok { httplog.LogOf(req, w).Addf("'%v' has no storage object", resource) notFound(w, req) httpCode = http.StatusNotFound return } apiResource = resource redirector, ok := storage.(Redirector) if !ok { httplog.LogOf(req, w).Addf("'%v' is not a redirector", resource) httpCode = errorJSON(errors.NewMethodNotSupported(resource, "proxy"), r.codec, w) return } location, err := redirector.ResourceLocation(ctx, id) if err != nil { httplog.LogOf(req, w).Addf("Error getting ResourceLocation: %v", err) status := errToAPIStatus(err) writeJSON(status.Code, r.codec, status, w) httpCode = status.Code return } destURL, err := url.Parse(location) if err != nil { status := errToAPIStatus(err) writeJSON(status.Code, r.codec, status, w) httpCode = status.Code return } if destURL.Scheme == "" { // If no scheme was present in location, url.Parse sometimes mistakes // hosts for paths. destURL.Host = location } destURL.Path = rest destURL.RawQuery = req.URL.RawQuery newReq, err := http.NewRequest(req.Method, destURL.String(), req.Body) if err != nil { status := errToAPIStatus(err) writeJSON(status.Code, r.codec, status, w) notFound(w, req) httpCode = status.Code return } httpCode = http.StatusOK newReq.Header = req.Header // TODO convert this entire proxy to an UpgradeAwareProxy similar to // https://github.com/openshift/origin/blob/master/pkg/util/httpproxy/upgradeawareproxy.go. // That proxy needs to be modified to support multiple backends, not just 1. if r.tryUpgrade(w, req, newReq, destURL) { return } proxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: "http", Host: destURL.Host}) proxy.Transport = &proxyTransport{ proxyScheme: req.URL.Scheme, proxyHost: req.URL.Host, proxyPathPrepend: requestInfo.URLPath(), } proxy.FlushInterval = 200 * time.Millisecond proxy.ServeHTTP(w, newReq) }
func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { namespace, kind, parts, err := KindAndNamespace(req) if err != nil { notFound(w, req) return } ctx := api.WithNamespace(api.NewContext(), namespace) if len(parts) < 2 { notFound(w, req) return } id := parts[1] rest := "" if len(parts) > 2 { proxyParts := parts[2:] rest = strings.Join(proxyParts, "/") } storage, ok := r.storage[kind] if !ok { httplog.LogOf(req, w).Addf("'%v' has no storage object", kind) notFound(w, req) return } redirector, ok := storage.(Redirector) if !ok { httplog.LogOf(req, w).Addf("'%v' is not a redirector", kind) errorJSON(errors.NewMethodNotSupported(kind, "proxy"), r.codec, w) return } location, err := redirector.ResourceLocation(ctx, id) if err != nil { httplog.LogOf(req, w).Addf("Error getting ResourceLocation: %v", err) status := errToAPIStatus(err) writeJSON(status.Code, r.codec, status, w) return } if location == "" { httplog.LogOf(req, w).Addf("ResourceLocation for %v returned ''", id) notFound(w, req) return } destURL, err := url.Parse(location) if err != nil { status := errToAPIStatus(err) writeJSON(status.Code, r.codec, status, w) return } if destURL.Scheme == "" { // If no scheme was present in location, url.Parse sometimes mistakes // hosts for paths. destURL.Host = location } destURL.Path = rest destURL.RawQuery = req.URL.RawQuery newReq, err := http.NewRequest(req.Method, destURL.String(), req.Body) if err != nil { status := errToAPIStatus(err) writeJSON(status.Code, r.codec, status, w) notFound(w, req) return } newReq.Header = req.Header proxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: "http", Host: destURL.Host}) proxy.Transport = &proxyTransport{ proxyScheme: req.URL.Scheme, proxyHost: req.URL.Host, proxyPathPrepend: path.Join(r.prefix, "ns", namespace, kind, id), } proxy.FlushInterval = 200 * time.Millisecond proxy.ServeHTTP(w, newReq) }
func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { proxyHandlerTraceID := rand.Int63() var verb string var apiResource string var httpCode int reqStart := time.Now() defer metrics.Monitor(&verb, &apiResource, util.GetClient(req), &httpCode, reqStart) requestInfo, err := r.apiRequestInfoResolver.GetAPIRequestInfo(req) if err != nil { notFound(w, req) httpCode = http.StatusNotFound return } verb = requestInfo.Verb namespace, resource, parts := requestInfo.Namespace, requestInfo.Resource, requestInfo.Parts ctx, ok := r.context.Get(req) if !ok { ctx = api.NewContext() } ctx = api.WithNamespace(ctx, namespace) if len(parts) < 2 { notFound(w, req) httpCode = http.StatusNotFound return } id := parts[1] remainder := "" if len(parts) > 2 { proxyParts := parts[2:] remainder = strings.Join(proxyParts, "/") if strings.HasSuffix(req.URL.Path, "/") { // The original path had a trailing slash, which has been stripped // by KindAndNamespace(). We should add it back because some // servers (like etcd) require it. remainder = remainder + "/" } } storage, ok := r.storage[resource] if !ok { httplog.LogOf(req, w).Addf("'%v' has no storage object", resource) notFound(w, req) httpCode = http.StatusNotFound return } apiResource = resource redirector, ok := storage.(rest.Redirector) if !ok { httplog.LogOf(req, w).Addf("'%v' is not a redirector", resource) httpCode = errorJSON(errors.NewMethodNotSupported(resource, "proxy"), r.codec, w) return } location, roundTripper, err := redirector.ResourceLocation(ctx, id) if err != nil { httplog.LogOf(req, w).Addf("Error getting ResourceLocation: %v", err) status := errToAPIStatus(err) writeJSON(status.Code, r.codec, status, w, true) httpCode = status.Code return } if location == nil { httplog.LogOf(req, w).Addf("ResourceLocation for %v returned nil", id) notFound(w, req) httpCode = http.StatusNotFound return } // If we have a custom dialer, and no pre-existing transport, initialize it to use the dialer. if roundTripper == nil && r.dial != nil { glog.V(5).Infof("[%x: %v] making a dial-only transport...", proxyHandlerTraceID, req.URL) roundTripper = &http.Transport{Dial: r.dial} } else if roundTripper != nil { glog.V(5).Infof("[%x: %v] using transport %T...", proxyHandlerTraceID, req.URL, roundTripper) } // Default to http if location.Scheme == "" { location.Scheme = "http" } // Add the subpath if len(remainder) > 0 { location.Path = singleJoiningSlash(location.Path, remainder) } // Start with anything returned from the storage, and add the original request's parameters values := location.Query() for k, vs := range req.URL.Query() { for _, v := range vs { values.Add(k, v) } } location.RawQuery = values.Encode() newReq, err := http.NewRequest(req.Method, location.String(), req.Body) if err != nil { status := errToAPIStatus(err) writeJSON(status.Code, r.codec, status, w, true) notFound(w, req) httpCode = status.Code return } httpCode = http.StatusOK newReq.Header = req.Header // TODO convert this entire proxy to an UpgradeAwareProxy similar to // https://github.com/openshift/origin/blob/master/pkg/util/httpproxy/upgradeawareproxy.go. // That proxy needs to be modified to support multiple backends, not just 1. if r.tryUpgrade(w, req, newReq, location, roundTripper) { return } // Redirect requests of the form "/{resource}/{name}" to "/{resource}/{name}/" // This is essentially a hack for https://github.com/GoogleCloudPlatform/kubernetes/issues/4958. // Note: Keep this code after tryUpgrade to not break that flow. if len(parts) == 2 && !strings.HasSuffix(req.URL.Path, "/") { var queryPart string if len(req.URL.RawQuery) > 0 { queryPart = "?" + req.URL.RawQuery } w.Header().Set("Location", req.URL.Path+"/"+queryPart) w.WriteHeader(http.StatusMovedPermanently) return } start := time.Now() glog.V(4).Infof("[%x] Beginning proxy %s...", proxyHandlerTraceID, req.URL) defer func() { glog.V(4).Infof("[%x] Proxy %v finished %v.", proxyHandlerTraceID, req.URL, time.Now().Sub(start)) }() proxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: location.Scheme, Host: location.Host}) alreadyRewriting := false if roundTripper != nil { _, alreadyRewriting = roundTripper.(*proxyutil.Transport) glog.V(5).Infof("[%x] Not making a reriting transport for proxy %s...", proxyHandlerTraceID, req.URL) } if !alreadyRewriting { glog.V(5).Infof("[%x] making a transport for proxy %s...", proxyHandlerTraceID, req.URL) prepend := path.Join(r.prefix, resource, id) if len(namespace) > 0 { prepend = path.Join(r.prefix, "namespaces", namespace, resource, id) } pTransport := &proxyutil.Transport{ Scheme: req.URL.Scheme, Host: req.URL.Host, PathPrepend: prepend, RoundTripper: roundTripper, } roundTripper = pTransport } proxy.Transport = roundTripper proxy.FlushInterval = 200 * time.Millisecond proxy.ServeHTTP(w, newReq) }
func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // use the default namespace to address the service ctx := api.NewDefaultContext() // if not in default namespace, provide the query parameter // TODO this will need to go in the path in the future and not as a query parameter namespace := req.URL.Query().Get("namespace") if len(namespace) > 0 { ctx = api.WithNamespace(ctx, namespace) } parts := strings.SplitN(req.URL.Path, "/", 3) if len(parts) < 2 { notFound(w, req) return } resourceName := parts[0] id := parts[1] rest := "" if len(parts) == 3 { rest = parts[2] } storage, ok := r.storage[resourceName] if !ok { httplog.LogOf(req, w).Addf("'%v' has no storage object", resourceName) notFound(w, req) return } redirector, ok := storage.(Redirector) if !ok { httplog.LogOf(req, w).Addf("'%v' is not a redirector", resourceName) notFound(w, req) return } location, err := redirector.ResourceLocation(ctx, id) if err != nil { httplog.LogOf(req, w).Addf("Error getting ResourceLocation: %v", err) status := errToAPIStatus(err) writeJSON(status.Code, r.codec, status, w) return } if location == "" { httplog.LogOf(req, w).Addf("ResourceLocation for %v returned ''", id) notFound(w, req) return } destURL, err := url.Parse(location) if err != nil { status := errToAPIStatus(err) writeJSON(status.Code, r.codec, status, w) return } if destURL.Scheme == "" { // If no scheme was present in location, url.Parse sometimes mistakes // hosts for paths. destURL.Host = location } destURL.Path = rest destURL.RawQuery = req.URL.RawQuery newReq, err := http.NewRequest(req.Method, destURL.String(), req.Body) if err != nil { status := errToAPIStatus(err) writeJSON(status.Code, r.codec, status, w) notFound(w, req) return } newReq.Header = req.Header proxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: "http", Host: destURL.Host}) proxy.Transport = &proxyTransport{ proxyScheme: req.URL.Scheme, proxyHost: req.URL.Host, proxyPathPrepend: path.Join(r.prefix, resourceName, id), } proxy.FlushInterval = 200 * time.Millisecond proxy.ServeHTTP(w, newReq) }