// ServeHTTP is called whenever there is a new request. // This is quite similar to JavaEE Servlet interface. func (h *MyHandler) ServeHTTP(ctx *fasthttp.RequestCtx) { // In the future we can use requester can detect request spammers! // requester := ctx.RemoteAddr() // Increase request count count := &(h.requests) atomic.AddUint64(count, 1) if ctx.IsGet() { url := ctx.URI() operations, format, err, invalid := ParseURI(url, h.imageSource, h.watermarker) if err != nil { ctx.NotFound() logging.Debug(err) return } if invalid != nil { ctx.Error(invalid.Error(), 400) return } blob, err := h.operator.GetBlob(operations...) if err != nil { ctx.NotFound() logging.Debug(err) } else { ctx.SetContentType("image/" + format) ctx.Write(blob) logging.Debug("Blob returned") } } else if ctx.IsPost() { // POST is currently unused so we can use this for testing h.RetrieveHello(ctx) logging.Debug("Post request received") } }
func mainHandler(ctx *fasthttp.RequestCtx) { // Performance hack for prefork mode - periodically close keepalive // connections for evenly distributing connections among available // processes. if *prefork { maxDuration := maxConnDuration + time.Millisecond*time.Duration(atomic.LoadUint64(&connDurationJitter)) if time.Since(ctx.ConnTime()) > maxDuration { atomic.StoreUint64(&connDurationJitter, uint64(rand.Intn(100))) ctx.SetConnectionClose() } } path := ctx.Path() switch string(path) { case "/plaintext": plaintextHandler(ctx) case "/json": jsonHandler(ctx) case "/db": dbHandler(ctx) case "/queries": queriesHandler(ctx) case "/fortune": fortuneHandler(ctx) case "/update": updateHandler(ctx) default: ctx.Error("unexpected path", fasthttp.StatusBadRequest) } }
func idHandlerFunc(ctx *fasthttp.RequestCtx, idWorker *goflake.IdWorker, retry int) { ua := string(ctx.UserAgent()) var ( id uint64 err error ) for i := 0; i < retry; i++ { id, err = idWorker.GetId(ua) if err == nil { break } } r := map[string]string{ "id": strconv.FormatUint(id, 10), } if strings.HasSuffix(string(ctx.Path()), ".msgpack") { ctx.SetContentType("application/x-msgpack; charset=UTF-8") if err := codec.NewEncoder(ctx, mh).Encode(r); err != nil { ctx.Error(err.Error(), fasthttp.StatusInternalServerError) } } else { ctx.SetContentType("application/json; charset=UTF-8") if err := json.NewEncoder(ctx).Encode(r); err != nil { ctx.Error(fmt.Sprintf(`{"error":"%v"}`, err.Error()), fasthttp.StatusInternalServerError) } } }
// GetPostJSON is the API method for getting a post's JSON. func GetPostJSON(ctx *fasthttp.RequestCtx, ps fasthttprouter.Params) { id := ps.ByName("id") postJSON, err := model.GetJSON([]byte(id), s) if err != nil { ctx.Error(err.Error(), fasthttp.StatusNotFound) return } ctx.Response.Header.Set("Content-Type", "application/json") ctx.Write(postJSON) }
// CheckHost Implement a CheckHost method on our new type func (hs HostSwitch) CheckHost(ctx *fasthttp.RequestCtx) { // Check if a http.Handler is registered for the given host. // If yes, use it to handle the request. if handler := hs[string(ctx.Host())]; handler != nil { handler(ctx) } else { // Handle host names for wich no handler is registered ctx.Error("Forbidden", 403) // Or Redirect? } }
func fastHTTPHandler(ctx *fasthttp.RequestCtx) { if string(ctx.Method()) == "GET" { switch string(ctx.Path()) { case "/rest/hello": ctx.Write([]byte("Hello world")) default: ctx.Error("Unsupported path", fasthttp.StatusNotFound) } return } }
//fasthttp func fastHttpRawHandler(ctx *fasthttp.RequestCtx) { if string(ctx.Method()) == "GET" { switch string(ctx.Path()) { case "/hello": if sleepTime > 0 { time.Sleep(sleepTimeDuration) } ctx.Write(message) default: ctx.Error("Unsupported path", fasthttp.StatusNotFound) } return } ctx.Error("Unsupported method", fasthttp.StatusMethodNotAllowed) }
// GetAllPosts is a method for getting every post func GetAllPosts(ctx *fasthttp.RequestCtx, _ fasthttprouter.Params) { posts, err := model.GetAll(s) if err != nil { ctx.Error(err.Error(), fasthttp.StatusNotFound) return } resp, err := json.MarshalIndent(posts, "", " ") if err != nil { ctx.Error(err.Error(), fasthttp.StatusInternalServerError) return } ctx.Response.Header.Set("Content-Type", "application/json") ctx.Write(resp) }
func mainHandler(ctx *fasthttp.RequestCtx) { path := ctx.Path() switch string(path) { case "/plaintext": plaintextHandler(ctx) case "/json": jsonHandler(ctx) case "/db": dbHandler(ctx) case "/queries": queriesHandler(ctx) case "/fortune": fortuneHandler(ctx) case "/update": updateHandler(ctx) default: ctx.Error("unexpected path", fasthttp.StatusBadRequest) } }
// SetPost is the API method for creating a new post. func SetPost(ctx *fasthttp.RequestCtx, _ fasthttprouter.Params) { v := &struct { Title string Body string }{} err := json.Unmarshal(ctx.PostBody(), v) if err != nil { ctx.Error(err.Error(), fasthttp.StatusInternalServerError) return } p := model.NewPost(v.Title, v.Body) id, err := p.Set(s) if err != nil { ctx.Error(err.Error(), fasthttp.StatusInternalServerError) return } ctx.Response.Header.Set("Content-Type", "application/json") resp, err := json.MarshalIndent(map[string]string{ "id": string(id), }, "", " ") if err != nil { ctx.Error(err.Error(), fasthttp.StatusInternalServerError) } ctx.Write(resp) }
// /raw/msgs/:topic/:ver func (this *Gateway) pubRawHandler(ctx *fasthttp.RequestCtx, params fasthttprouter.Params) { var ( topic string ver string appid string pubkey string ) ver = params.ByName(UrlParamVersion) topic = params.ByName(UrlParamTopic) header := ctx.Request.Header appid = string(header.Peek(HttpHeaderAppid)) pubkey = string(header.Peek(HttpHeaderPubkey)) if err := manager.Default.OwnTopic(appid, pubkey, topic); err != nil { log.Error("app[%s] %s %+v: %v", appid, ctx.RemoteAddr(), params, err) ctx.SetConnectionClose() ctx.Error("invalid secret", fasthttp.StatusUnauthorized) return } cluster, found := manager.Default.LookupCluster(appid) if !found { log.Error("cluster not found for app: %s", appid) ctx.Error("invalid appid", fasthttp.StatusBadRequest) return } var out = map[string]string{ "store": "kafka", "broker.list": strings.Join(meta.Default.BrokerList(cluster), ","), "topic": manager.Default.KafkaTopic(appid, topic, ver), } b, _ := json.Marshal(out) ctx.SetContentType("application/json; charset=utf8") ctx.Write(b) }
func requestHandler(ctx *fasthttp.RequestCtx) { headers := http.Header{} ctx.Request.Header.VisitAll(func(key, value []byte) { headers.Add(string(key), string(value)) }) u, err := url.Parse(fmt.Sprint("http://", *upstreamHost, "/", string(ctx.Request.RequestURI()))) if err != nil { ctx.Error("Bad Gateway", fasthttp.StatusBadGateway) log.Print(err) return } req := &http.Request{ Method: string(ctx.Method()), URL: u, Proto: map[bool]string{true: "HTTP/1.1", false: "HTTP/1.0"}[ctx.Request.Header.IsHTTP11()], ProtoMinor: map[bool]int{true: 1, false: 0}[ctx.Request.Header.IsHTTP11()], Header: headers, Body: ioutil.NopCloser(bytes.NewReader(ctx.Request.Body())), ContentLength: int64(ctx.Request.Header.ContentLength()), Host: string(ctx.Request.Header.Host()), } resp, err := upstreamClient.Do(req) if err != nil { ctx.Error("Bad Gateway", fasthttp.StatusBadGateway) log.Print(err) return } defer resp.Body.Close() for k, v := range resp.Header { ctx.Response.Header.Add(k, strings.Join(v, ",")) } io.Copy(ctx.Response.BodyWriter(), resp.Body) }
func (a *API) Handler(ctx *fasthttp.RequestCtx) { ctx.SetContentType("application/json") ctx.Response.Header.Set("Access-Control-Allow-Origin", "*") switch string(ctx.Path()) { case "/rules": j, err := json.Marshal(a.Proxy.Rules) if err != nil { ctx.Error(fmt.Sprintf("{\"error\": \"%v\"}", err), 500) return } ctx.Write(j) case "/rules/reload": if err := a.Proxy.ReloadRules(a.RuleFile); err != nil { ctx.Error(fmt.Sprintf("{\"error\": \"%v\"}", err), 500) return } log.Println("Rule file reloaded") ctx.Write([]byte("{\"status\": \"ok\"}")) default: ctx.Error("{\"error\": \"Not found\"}", fasthttp.StatusNotFound) } }
// /ws/topics/:topic/:ver func (this *Gateway) pubWsHandler(ctx *fasthttp.RequestCtx, params fasthttprouter.Params) { ctx.Error("not implemented", fasthttp.StatusBadRequest) }
// Handler makes the router implement the fasthttp.ListenAndServe interface. func (r *Router) Handler(ctx *fasthttp.RequestCtx) { if r.PanicHandler != nil { defer r.recv(ctx) } path := string(ctx.Path()) method := string(ctx.Method()) if root := r.trees[method]; root != nil { if f, ps, tsr := root.getValue(path); f != nil { f(ctx, ps) return } else if method != "CONNECT" && path != "/" { code := 301 // Permanent redirect, request with GET method if method != "GET" { // Temporary redirect, request with same method // As of Go 1.3, Go does not support status code 308. code = 307 } if tsr && r.RedirectTrailingSlash { var uri string if len(path) > 1 && path[len(path)-1] == '/' { uri = path[:len(path)-1] } else { uri = path + "/" } ctx.Redirect(uri, code) return } // Try to fix the request path if r.RedirectFixedPath { fixedPath, found := root.findCaseInsensitivePath( CleanPath(path), r.RedirectTrailingSlash, ) if found { uri := string(fixedPath) ctx.Redirect(uri, code) return } } } } if method == "OPTIONS" { // Handle OPTIONS requests if r.HandleOPTIONS { if allow := r.allowed(path, method); len(allow) > 0 { ctx.Response.Header.Set("Allow", allow) return } } } else { // Handle 405 if r.HandleMethodNotAllowed { if allow := r.allowed(path, method); len(allow) > 0 { ctx.Response.Header.Set("Allow", allow) if r.MethodNotAllowed != nil { r.MethodNotAllowed(ctx) } else { ctx.SetStatusCode(fasthttp.StatusMethodNotAllowed) ctx.SetContentTypeBytes(defaultContentType) ctx.SetBodyString(fasthttp.StatusMessage(fasthttp.StatusMethodNotAllowed)) } return } } } // Handle 404 if r.NotFound != nil { r.NotFound(ctx) } else { ctx.Error(fasthttp.StatusMessage(fasthttp.StatusNotFound), fasthttp.StatusNotFound) } }
func requestHandler(ctx *fasthttp.RequestCtx) { h := &ctx.Request.Header if !ctx.IsGet() { ctx.Error("Method not allowed", fasthttp.StatusMethodNotAllowed) return } if string(ctx.RequestURI()) == *statsRequestPath { var w bytes.Buffer stats.WriteToStream(&w) ctx.Success("text/plain", w.Bytes()) return } if len(h.Peek("If-None-Match")) > 0 { resp := &ctx.Response resp.SetStatusCode(fasthttp.StatusNotModified) resp.Header.Set("Etag", "W/\"CacheForever\"") atomic.AddInt64(&stats.IfNoneMatchHitsCount, 1) return } v := keyPool.Get() if v == nil { v = make([]byte, 128) } key := v.([]byte) key = append(key[:0], getRequestHost(h)...) key = append(key, ctx.RequestURI()...) item, err := cache.GetDeItem(key, time.Second) if err != nil { if err != ybc.ErrCacheMiss { logFatal("Unexpected error when obtaining cache value by key=[%s]: [%s]", key, err) } atomic.AddInt64(&stats.CacheMissesCount, 1) item = fetchFromUpstream(h, key) if item == nil { ctx.Error("Service unavailable", fasthttp.StatusServiceUnavailable) return } } else { atomic.AddInt64(&stats.CacheHitsCount, 1) } defer item.Close() keyPool.Put(v) contentType, err := loadContentType(h, item) if err != nil { ctx.Error("Internal Server Error", fasthttp.StatusInternalServerError) return } rh := &ctx.Response.Header rh.Set("Etag", "W/\"CacheForever\"") rh.Set("Cache-Control", "public, max-age=31536000") buf := item.Value() buf = buf[len(buf)-item.Available():] ctx.Success(contentType, buf) atomic.AddInt64(&stats.BytesSentToClients, int64(len(buf))) }
// /topics/:topic/:ver?key=mykey&async=1&delay=100 func (this *Gateway) pubHandler(ctx *fasthttp.RequestCtx, params fasthttprouter.Params) { t1 := time.Now() topic := params.ByName(UrlParamTopic) header := ctx.Request.Header appid := string(header.Peek(HttpHeaderAppid)) pubkey := string(header.Peek(HttpHeaderPubkey)) if err := manager.Default.OwnTopic(appid, pubkey, topic); err != nil { log.Error("app[%s] %s %+v: %v", appid, ctx.RemoteAddr(), params, err) ctx.SetConnectionClose() ctx.Error("invalid secret", fasthttp.StatusUnauthorized) return } msgLen := ctx.Request.Header.ContentLength() switch { case msgLen == -1: log.Warn("pub[%s] %s %+v invalid content length", appid, ctx.RemoteAddr(), params) ctx.Error("invalid content length", fasthttp.StatusBadRequest) return case int64(msgLen) > options.MaxPubSize: log.Warn("pub[%s] %s %+v too big content length:%d", appid, ctx.RemoteAddr(), params, msgLen) ctx.Error(ErrTooBigPubMessage.Error(), fasthttp.StatusBadRequest) return case msgLen < options.MinPubSize: log.Warn("pub[%s] %s %+v too small content length:%d", appid, ctx.RemoteAddr(), params, msgLen) ctx.Error(ErrTooSmallPubMessage.Error(), fasthttp.StatusBadRequest) return } ver := params.ByName(UrlParamVersion) queryArgs := ctx.Request.URI().QueryArgs() key := queryArgs.Peek("key") asyncArg := queryArgs.Peek("async") async := len(asyncArg) == 1 && asyncArg[0] == '1' //delay := hack.String(queryArgs.Peek("delay")) if options.Debug { log.Debug("pub[%s] %s {topic:%s, ver:%s, key:%s, async:%+v} %s", appid, ctx.RemoteAddr(), topic, ver, key, async, string(ctx.Request.Body())) } if !options.DisableMetrics { this.pubMetrics.PubQps.Mark(1) this.pubMetrics.PubMsgSize.Update(int64(len(ctx.PostBody()))) } pubMethod := store.DefaultPubStore.SyncPub if async { pubMethod = store.DefaultPubStore.AsyncPub } cluster, found := manager.Default.LookupCluster(appid) if !found { log.Error("cluster not found for app: %s", appid) ctx.Error("invalid appid", fasthttp.StatusBadRequest) return } err := pubMethod(cluster, manager.Default.KafkaTopic(appid, topic, ver), key, ctx.PostBody()) if err != nil { if !options.DisableMetrics { this.pubMetrics.PubFail(appid, topic, ver) } log.Error("%s: %v", ctx.RemoteAddr(), err) ctx.Error(err.Error(), fasthttp.StatusInternalServerError) return } // write the reponse ctx.Write(ResponseOk) if !options.DisableMetrics { this.pubMetrics.PubOk(appid, topic, ver) this.pubMetrics.PubLatency.Update(time.Since(t1).Nanoseconds() / 1e6) // in ms } }
// Handler makes the router implement the fasthttp.ListenAndServe interface. func (r *Router) Handler(ctx *fasthttp.RequestCtx) { if r.PanicHandler != nil { defer r.recv(ctx) } method := string(ctx.Method()) if root := r.trees[method]; root != nil { path := string(ctx.Path()) if f, ps, tsr := root.getValue(path); f != nil { f(ctx, ps) return } else if method != "CONNECT" && path != "/" { code := 301 // Permanent redirect, request with GET method if method != "GET" { // Temporary redirect, request with same method // As of Go 1.3, Go does not support status code 308. code = 307 } if tsr && r.RedirectTrailingSlash { var uri string if len(path) > 1 && path[len(path)-1] == '/' { uri = path[:len(path)-1] } else { uri = path + "/" } ctx.Redirect(uri, code) return } // Try to fix the request path if r.RedirectFixedPath { fixedPath, found := root.findCaseInsensitivePath( CleanPath(path), r.RedirectTrailingSlash, ) if found { uri := string(fixedPath) ctx.Redirect(uri, code) return } } } } // Handle 405 if r.HandleMethodNotAllowed { for method := range r.trees { // Skip the requested method - we already tried this one if method == string(ctx.Method()) { continue } f, _, _ := r.trees[method].getValue(string(ctx.Path())) if f != nil { if r.MethodNotAllowed != nil { r.MethodNotAllowed(ctx) } else { ctx.Error(fasthttp.StatusMessage(fasthttp.StatusMethodNotAllowed), fasthttp.StatusMethodNotAllowed) } return } } } // Handle 404 if r.NotFound != nil { r.NotFound(ctx) } else { ctx.Error(fasthttp.StatusMessage(fasthttp.StatusNotFound), fasthttp.StatusNotFound) } }