Skip to content

Commit

Permalink
Merge pull request #6 from ninech/last-modified-header
Browse files Browse the repository at this point in the history
feat: set last-modified header with build time
  • Loading branch information
ctrox authored Dec 19, 2023
2 parents 3028d4f + 701d1bd commit dfb7ed7
Show file tree
Hide file tree
Showing 4 changed files with 382 additions and 10 deletions.
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

0 comments on commit dfb7ed7

Please sign in to comment.