Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: set last-modified header with build time #6

Merged
merged 1 commit into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
227 changes: 227 additions & 0 deletions assets/default.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
# Number of worker processes running in container
worker_processes 1;

# Run NGINX in foreground (necessary for containerized NGINX)
daemon off;

# Set the location of the server's error log
error_log stderr;

events {
# Set number of simultaneous connections each worker process can serve
worker_connections 1024;
}

http {
client_body_temp_path {{ tempDir }}/client_body_temp;
proxy_temp_path {{ tempDir }}/proxy_temp;
fastcgi_temp_path {{ tempDir }}/fastcgi_temp;

charset utf-8;

# Map media types to file extensions
types {
text/html html htm shtml;
text/css css;
text/xml xml;
image/gif gif;
image/jpeg jpeg jpg;
application/javascript js;
application/atom+xml atom;
application/rss+xml rss;
font/ttf ttf;
font/woff woff;
font/woff2 woff2;
text/mathml mml;
text/plain txt;
text/vnd.sun.j2me.app-descriptor jad;
text/vnd.wap.wml wml;
text/x-component htc;
text/cache-manifest manifest;
image/png png;
image/tiff tif tiff;
image/vnd.wap.wbmp wbmp;
image/x-icon ico;
image/x-jng jng;
image/x-ms-bmp bmp;
image/svg+xml svg svgz;
image/webp webp;
application/java-archive jar war ear;
application/mac-binhex40 hqx;
application/msword doc;
application/pdf pdf;
application/postscript ps eps ai;
application/rtf rtf;
application/vnd.ms-excel xls;
application/vnd.ms-powerpoint ppt;
application/vnd.wap.wmlc wmlc;
application/vnd.google-earth.kml+xml kml;
application/vnd.google-earth.kmz kmz;
application/x-7z-compressed 7z;
application/x-cocoa cco;
application/x-java-archive-diff jardiff;
application/x-java-jnlp-file jnlp;
application/x-makeself run;
application/x-perl pl pm;
application/x-pilot prc pdb;
application/x-rar-compressed rar;
application/x-redhat-package-manager rpm;
application/x-sea sea;
application/x-shockwave-flash swf;
application/x-stuffit sit;
application/x-tcl tcl tk;
application/x-x509-ca-cert der pem crt;
application/x-xpinstall xpi;
application/xhtml+xml xhtml;
application/zip zip;
application/octet-stream bin exe dll;
application/octet-stream deb;
application/octet-stream dmg;
application/octet-stream eot;
application/octet-stream iso img;
application/octet-stream msi msp msm;
application/json json;
audio/midi mid midi kar;
audio/mpeg mp3;
audio/ogg ogg;
audio/x-m4a m4a;
audio/x-realaudio ra;
video/3gpp 3gpp 3gp;
video/mp4 mp4;
video/mpeg mpeg mpg;
video/quicktime mov;
video/webm webm;
video/x-flv flv;
video/x-m4v m4v;
video/x-mng mng;
video/x-ms-asf asx asf;
video/x-ms-wmv wmv;
video/x-msvideo avi;
}

access_log /dev/stdout;

# Set the default MIME type of responses; 'application/octet-stream'
# represents an arbitrary byte stream
default_type application/octet-stream;

# (Performance) When sending files, skip copying into buffer before sending.
sendfile on;
# (Only active with sendfile on) wait for packets to reach max size before
# sending.
tcp_nopush on;

# (Performance) Enable compressing responses
gzip on;
# For all clients
gzip_static always;
# Including responses to proxied requests
gzip_proxied any;
# For responses above a certain length
gzip_min_length 1100;
# That are one of the following MIME types
gzip_types
text/plain
text/css
text/js
text/xml
text/javascript
application/javascript
application/x-javascript
application/json
application/xml
application/xml+rss
font/eot
font/otf
font/ttf
image/svg+xml;
# Compress responses to a medium degree
gzip_comp_level 6;
# Using 16 buffers of 8k bytes each
gzip_buffers 16 8k;

# Add "Vary: Accept-Encoding” response header to compressed responses
gzip_vary on;

# Decompress responses if client doesn't support compressed
gunzip on;

# Don't compress responses if client is Internet Explorer 6
gzip_disable "msie6";

# Set a timeout during which a keep-alive client connection will stay open on
# the server side
keepalive_timeout 30;

# Ensure that redirects don't include the internal container PORT - <%=
# ENV["PORT"] %>
port_in_redirect off;

# (Security) Disable emitting nginx version on error pages and in the
# “Server” response header field
server_tokens off;

server {
listen {{port}} default_server;
server_name _;

# Directory where static files are located
root $(( .WebServerRoot -));
$(( if .WebServerForceHTTPS ))
# If HTTP request is made, redirect to HTTPS requests
set $updated_host $host;
if ($http_x_forwarded_host != "") {
set $updated_host $http_x_forwarded_host;
}

if ($http_x_forwarded_proto != "https") {
return 301 https://$updated_host$request_uri;
}
$(( end ))
$((- if (ne .BasicAuthFile "") ))
# Require username + password authentication for access
auth_basic "Password Protected";
auth_basic_user_file $(( .BasicAuthFile ));
$(( end ))
location $(( .WebServerLocationPath )) {
$((- if .WebServerEnablePushState ))
# Send the content at / in response to *any* requested endpoint
if (!-e $request_filename) {
rewrite ^(.*)$ / break;
}
$(( end ))
# Specify files sent to client if specific file not requested (e.g.
# GET www.example.com/). NGINX sends first existing file in the list.
index index.html index.htm Default.htm;
}

$((- if .LastModifiedValue ))
add_header last-modified '$(( .LastModifiedValue ))';
$(( end ))

$((- if .ETag ))
etag 'on';
$(( else ))
etag 'off';
$(( end ))

# (Security) Don't serve dotfiles, except .well-known/, which is needed by
# LetsEncrypt
location ~ /\.(?!well-known) {
deny all;
return 404;
}
}

$(( if .NGINXStubStatusPort ))
# stub_status
server {
listen $(( .NGINXStubStatusPort -));
listen [::]:$(( .NGINXStubStatusPort -));

location /stub_status {
stub_status;
}
}
$(( end ))
}
17 changes: 13 additions & 4 deletions build.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package static
import (
"errors"
"fmt"
"net/http"
"os"
"path/filepath"
"time"

"github.com/paketo-buildpacks/nginx"
"github.com/paketo-buildpacks/packit/v2"
Expand All @@ -29,10 +31,17 @@ func Build(logger scribe.Emitter) packit.BuildFunc {
nginxConf := filepath.Join(context.WorkingDir, nginx.ConfFile)
if _, err := os.Stat(nginxConf); err != nil {
if errors.Is(err, os.ErrNotExist) {
confGen := nginx.NewDefaultConfigGenerator(logger)
if err := confGen.Generate(nginx.Configuration{
NGINXConfLocation: nginxConf,
WebServerRoot: webRoot,
confGen := NewDefaultConfigGenerator(logger)
if err := confGen.Generate(Configuration{
// we set the last-modified header to the current time
// during build. This works around the issue described in:
// https://github.com/paketo-buildpacks/nginx/issues/447
LastModifiedValue: time.Now().UTC().Format(http.TimeFormat),
ETag: false,
Configuration: nginx.Configuration{
NGINXConfLocation: nginxConf,
WebServerRoot: webRoot,
},
}); err != nil {
return packit.BuildResult{}, packit.Fail.WithMessage("unable to create nginx.conf: %s", err)
}
Expand Down
88 changes: 88 additions & 0 deletions default_config_generator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package static

import (
"bytes"
_ "embed"
"fmt"
"html/template"
"io"
"os"
"path/filepath"

"github.com/paketo-buildpacks/nginx"
"github.com/paketo-buildpacks/packit/v2/scribe"
)

//go:embed assets/default.conf
var DefaultConfigTemplate string

// this has been adapted from https://github.com/paketo-buildpacks/nginx/blob/main/default_config_generator.go
// to add our own customizations to the config
type DefaultConfigGenerator struct {
logs scribe.Emitter
}

type Configuration struct {
LastModifiedValue string
ETag bool
nginx.Configuration
}

func NewDefaultConfigGenerator(logs scribe.Emitter) DefaultConfigGenerator {
return DefaultConfigGenerator{logs: logs}
}

func (g DefaultConfigGenerator) Generate(config Configuration) error {
g.logs.Process("Generating %s", config.NGINXConfLocation)
t := template.Must(template.New("template.conf").Delims("$((", "))").Parse(DefaultConfigTemplate))

if !filepath.IsAbs(config.WebServerRoot) {
config.WebServerRoot = filepath.Join(`{{ env "APP_ROOT" }}`, config.WebServerRoot)
}

g.logs.Subprocess("Setting server root directory to '%s'", config.WebServerRoot)

if config.WebServerLocationPath == "" {
config.WebServerLocationPath = "/"
}

g.logs.Subprocess("Setting server location path to '%s'", config.WebServerLocationPath)

if config.WebServerEnablePushState {
g.logs.Subprocess("Enabling push state routing")
}

if config.WebServerForceHTTPS {
g.logs.Subprocess("Setting server to redirect HTTP requests to HTTPS")
}

if config.BasicAuthFile != "" {
g.logs.Subprocess("Enabling basic authentication with .htpasswd credentials")
}

if config.NGINXStubStatusPort != "" {
g.logs.Subprocess("Enabling basic status information with stub_status module")
}

g.logs.Break()

var b bytes.Buffer
err := t.Execute(&b, config)
if err != nil {
// not tested
return err
}

f, err := os.OpenFile(config.NGINXConfLocation, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm)
if err != nil {
return fmt.Errorf("failed to create %s: %w", config.NGINXConfLocation, err)
}
defer f.Close()

_, err = io.Copy(f, &b)
if err != nil {
// not tested
return err
}
return nil
}
Loading