// FromFont initializes ff from the given font. If it is called on an already // initialized font face, changes the font face according to the given font. // Returns an error if the initialization fails. func (ff *FontFace) FromFont(f font.Font) error { data, err := f.Contents() if err != nil { return err } mimeType := f.MimeType() if mimeType == "" { // Should not happen. return fmt.Errorf("can't determine font MIME type") } ff.Base64Data = util.Base64(data) ff.Family = f.Family ff.Format = f.Format.String() ff.MimeType = mimeType ff.Style = f.Style ff.Weight = f.Weight return nil }
func TestFontFaceFromFont(t *testing.T) { ff := &FontFace{} f := font.Font{ Family: "Amaranth", Format: font.WOFF, Path: filepath.Join(amf, "amaranth-regular.woff"), Style: "normal", Weight: 400, } err := ff.FromFont(f) test.VerifyFatal(t, 1, 0, true, nil == err) data, err := f.Contents() test.Verify(t, 2, 0, util.Base64(data), ff.Base64Data) test.Verify(t, 3, 0, f.Family, ff.Family) test.Verify(t, 4, 0, f.Format.String(), ff.Format) wMimeType := f.MimeType() test.VerifyFatal(t, 5, 0, false, "" == wMimeType) test.Verify(t, 6, 0, wMimeType, ff.MimeType) test.Verify(t, 7, 0, f.Style, ff.Style) test.Verify(t, 8, 0, f.Weight, ff.Weight) }
func TestCssHandler(t *testing.T) { type Request struct { Method string URL string } // Build an inventory. // Used in cases 6-7. inv := inventory.New() err := inv.Build(fl) test.VerifyFatal(t, 1, 0, true, err == nil) // Build an "allow all" whitelist. // Use this whitelist to allow all // referrers to fetch the resource. // Used in cases 3-7. aawl := whitelist.New() aawl.Domains = append(aawl.Domains, "") // Parse templates. // Used in cases 7-9. eot := filepath.Join(tp, "eot.css.tmpl") woff := filepath.Join(tp, "woff.css.tmpl") tmpl, err := template.ParseFiles(eot, woff) test.VerifyFatal(t, 2, 0, true, err == nil) // Execute template containing Amaranth Regular. // Used in case 7. ar := &font.Font{ Family: "Amaranth", Format: font.WOFF, Path: filepath.Join(amf, "amaranth-regular.woff"), Style: "normal", Weight: 400, } var tmplData []*FontFace data, err := ar.Contents() test.VerifyFatal(t, 3, 0, true, err == nil) mimeType := ar.MimeType() test.VerifyFatal(t, 4, 0, false, "" == mimeType) arff := &FontFace{ Base64Data: util.Base64(data), Family: ar.Family, Format: ar.Format.String(), MimeType: mimeType, Style: ar.Style, Weight: ar.Weight, } buf := new(bytes.Buffer) tmplName := ar.Format.String() + ".css.tmpl" tmplData = append(tmplData, arff) err = tmpl.ExecuteTemplate(buf, tmplName, tmplData) test.VerifyFatal(t, 5, 0, true, nil == err) arBody := buf.Bytes() // Generate the entity tag corresponding to Amaranth Regular. // Uncompressed response. // Used in case 7. hash := md5.New() modtime, err := ar.ModTime() test.VerifyFatal(t, 6, 0, true, nil == err) io.WriteString(hash, modtime.String()) arEtag := fmt.Sprintf("%x", hash.Sum(nil)) // Gzip compressed response. // Used in cases 8-9. arEtagGzip := arEtag + "+gzip" var cases = []struct { Body []byte Context HandlerContext Header map[string]string IfNoneMatch string // If-None-Match client request header. Request *Request StatusCode int }{ // Case 1 { Header: map[string]string{ "Content-Type": "text/plain; charset=utf-8", }, Request: &Request{ Method: "POST", URL: "", }, StatusCode: http.StatusNotImplemented, }, // Case 2 { Header: map[string]string{ "Content-Type": "text/plain; charset=utf-8", }, Request: &Request{ Method: "GET", URL: "", }, StatusCode: http.StatusForbidden, }, // Case 3 { Context: HandlerContext{ Whitelist: *aawl, }, Header: map[string]string{ "Content-Type": "text/plain; charset=utf-8", }, Request: &Request{ Method: "GET", URL: "?family=", }, StatusCode: http.StatusBadRequest, }, // Case 4 { Context: HandlerContext{ Whitelist: *aawl, }, Header: map[string]string{ "Content-Type": "text/plain; charset=utf-8", }, Request: &Request{ Method: "GET", URL: "?family=Amaranth&format=nonexistent", }, StatusCode: http.StatusBadRequest, }, // Case 5 { Context: HandlerContext{ Whitelist: *aawl, }, Header: map[string]string{ "Content-Type": "text/plain; charset=utf-8", }, Request: &Request{ Method: "GET", URL: "?family=|Amaranth", }, StatusCode: http.StatusBadRequest, }, // Case 6 { Context: HandlerContext{ Flags: Flags{ Etag: true, }, Inventory: *inv, Whitelist: *aawl, }, Header: map[string]string{ "Content-Type": "text/plain; charset=utf-8", "Etag": "", }, Request: &Request{ Method: "GET", URL: "?family=Nonexistent", }, StatusCode: http.StatusBadRequest, }, // Case 7 { Body: arBody, Context: HandlerContext{ Flags: Flags{ CcMaxAge: 2592000, Etag: true, }, Inventory: *inv, Templates: *tmpl, Whitelist: *aawl, }, Header: map[string]string{ "Cache-Control": "max-age=2592000", "Content-Type": "text/css; charset=utf-8", "Etag": arEtag, }, Request: &Request{ Method: "GET", URL: "?family=Amaranth", }, StatusCode: http.StatusOK, }, // Case 8 { Body: arBody, Context: HandlerContext{ Flags: Flags{ Etag: true, Gzip: true, }, Inventory: *inv, Templates: *tmpl, Whitelist: *aawl, }, Header: map[string]string{ "Cache-Control": "max-age=0", "Content-Type": "text/css; charset=utf-8", "Etag": arEtagGzip, }, Request: &Request{ Method: "GET", URL: "?family=Amaranth", }, StatusCode: http.StatusOK, }, // Case 9 { Context: HandlerContext{ Flags: Flags{ Etag: true, Gzip: true, }, Inventory: *inv, Templates: *tmpl, Whitelist: *aawl, }, Header: map[string]string{ "Cache-Control": "max-age=0", "Etag": arEtagGzip, }, IfNoneMatch: arEtagGzip, Request: &Request{ Method: "GET", URL: "?family=Amaranth", }, StatusCode: http.StatusNotModified, }, } for i, c := range cases { j := i + 1 handler := MakeHandler(CssHandler, c.Context) server := httptest.NewServer(handler) defer server.Close() client := http.Client{} reqURL := server.URL + c.Request.URL req, err := http.NewRequest(c.Request.Method, reqURL, nil) test.VerifyFatal(t, 6, j, true, nil == err) if c.IfNoneMatch != "" { req.Header.Add("If-None-Match", c.IfNoneMatch) } resp, err := client.Do(req) defer resp.Body.Close() test.VerifyFatal(t, 7, j, true, nil == err) test.Verify(t, 8, j, c.StatusCode, resp.StatusCode) wContentType := c.Header["Content-Type"] gContentType := resp.Header.Get("Content-Type") test.Verify(t, 9, j, wContentType, gContentType) wCacheControl := c.Header["Cache-Control"] gCacheControl := resp.Header.Get("Cache-Control") test.Verify(t, 10, j, wCacheControl, gCacheControl) wEtag := c.Header["Etag"] gEtag := resp.Header.Get("Etag") test.Verify(t, 11, j, wEtag, gEtag) if (c.StatusCode == http.StatusOK) && (resp.StatusCode == http.StatusOK) { gbody, err := ioutil.ReadAll(resp.Body) test.VerifyFatal(t, 13, j, true, nil == err) wbody := c.Body test.Verify(t, 14, j, true, bytes.Equal(wbody, gbody)) } } }